You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对ORM调用或查询构建器进行单元测试(Laravel无DB Mock方案)

Laravel 框架下的 Mock 单元测试实现

针对你提供的getAllotedSectionsToStaff方法,我们可以通过Mock TeacherSubject 模型验证查询逻辑的正确性,无需连接真实数据库。以下是具体测试代码(基于 Laravel 自带的 PHPUnit + Mockery):

use Tests\TestCase;
use App\Models\TeacherSubject;
use Illuminate\Database\Eloquent\Builder;

class YourClassNameTest extends TestCase
{
    public function test_get_alloted_sections_to_staff_uses_correct_logic()
    {
        // 准备测试参数
        $staffId = 1;
        $classId = 2;

        // Mock Eloquent Builder 实例,模拟链式调用
        $builderMock = $this->createMock(Builder::class);
        
        // 验证 where 条件是否正确传入
        $builderMock->expects($this->once())
            ->method('where')
            ->with([
                'school_class_id' => $classId,
                'staff_id' => $staffId
            ])
            ->willReturnSelf();

        // 验证是否调用了关联预加载 with('section')
        $builderMock->expects($this->once())
            ->method('with')
            ->with('section')
            ->willReturnSelf();

        // 验证是否按 section_id 分组
        $builderMock->expects($this->once())
            ->method('groupBy')
            ->with('section_id')
            ->willReturnSelf();

        // 验证最终调用了 get() 方法
        $builderMock->expects($this->once())
            ->method('get')
            ->willReturn(collect());

        // 让 TeacherSubject 静态调用 where 时返回我们的 Mock
        $this->mock(TeacherSubject::class, function ($mock) use ($builderMock) {
            $mock->shouldReceive('where')
                ->andReturn($builderMock);
        });

        // 调用待测试方法
        $result = $this->yourClassInstance->getAllotedSectionsToStaff($staffId, $classId);

        // 验证返回结果类型
        $this->assertInstanceOf(\Illuminate\Support\Collection::class, $result);
    }
}

测试逻辑说明

  • Mock Eloquent的Builder类,模拟链式调用的每一步
  • 逐一验证where、with('section')、groupBy('section_id')、get()方法的调用及参数正确性
  • 完全脱离真实数据库,仅验证代码逻辑的规范性

原生PHP环境下的解决方案建议

如果是在非Laravel的原生PHP环境中,可直接使用PHPUnit的MockBuilder模拟模型类及其链式调用:

use PHPUnit\Framework\TestCase;
use YourNamespace\Models\TeacherSubject;

class YourClassNameTest extends TestCase
{
    public function test_get_alloted_sections_to_staff()
    {
        $staffId = 1;
        $classId = 2;

        // Mock TeacherSubject 模型
        $teacherSubjectMock = $this->getMockBuilder(TeacherSubject::class)
            ->disableOriginalConstructor()
            ->getMock();

        // Mock 链式调用的中间实例
        $builderMock = $this->getMockBuilder(stdClass::class)
            ->addMethods(['where', 'with', 'groupBy', 'get'])
            ->getMock();

        // 验证 where 条件
        $builderMock->expects($this->once())
            ->method('where')
            ->with([
                'school_class_id' => $classId,
                'staff_id' => $staffId
            ])
            ->willReturnSelf();

        // 验证关联预加载
        $builderMock->expects($this->once())
            ->method('with')
            ->with('section')
            ->willReturnSelf();

        // 验证分组条件
        $builderMock->expects($this->once())
            ->method('groupBy')
            ->with('section_id')
            ->willReturnSelf();

        // 验证 get() 调用
        $builderMock->expects($this->once())
            ->method('get')
            ->willReturn([]);

        // 让 TeacherSubject 的静态 where 返回 Builder Mock
        $teacherSubjectMock::staticExpects($this->once())
            ->method('where')
            ->willReturn($builderMock);

        // 调用测试方法
        $result = $yourClassInstance->getAllotedSectionsToStaff($staffId, $classId);

        $this->assertIsArray($result);
    }
}

原生PHP注意事项

  • 若模型使用ORM链式调用,需确保每个链式方法返回自身(willReturnSelf())以维持调用链
  • 静态方法的Mock需使用staticExpects(PHPUnit旧版),新版可通过getMockBuilder的onlyMethods指定静态方法进行Mock

内容的提问来源于stack exchange,提问作者Gaurav Rai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 08:12:27