如何对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
相关产品推荐
相关产品推荐

