Symfony 2 单元测试应隔离验证查询逻辑,通过 mock Repository 接口(如 UserRepositoryInterface)断言服务行为,而非连接真实数据库;禁止在单元测试中使用 SQLite、SchemaTool 或启动内核,DQL 正确性需另写集成测试。

Symfony 2 的数据库查询逻辑不应放在控制器或 Repository 内直接写 SQL 或耦合 EntityManager,单元测试更不该操作真实数据库。所谓“数据库查询单元测试”,本质是对查询逻辑的隔离验证——比如一个服务类调用了 Repository 的某个方法,你只需 mock 这个 Repository 方法的返回值,再断言服务行为是否正确,而不是去测 Doctrine 怎么查库。
Repository 方法必须可被 Mock
确保你的 Repository 被定义为接口(如 UserRepositoryInterface),并在服务中依赖该接口而非具体实现:
- 控制器或服务通过构造函数注入
UserRepositoryInterface - 测试时用
$this->createMock(UserRepositoryInterface::class)创建模拟对象 - 设置期望返回值:
$mockRepo->method('findActiveUsers')->willReturn([$user1, $user2]) - 避免在 Repository 构造函数里初始化连接、加载配置或调用外部服务
测试服务层的查询调用逻辑
假设你有个 UserStatsService,它调用 UserRepositoryInterface::findActiveUsers() 后做统计。单元测试应聚焦它“是否调用了正确方法”和“是否正确处理了返回数据”:
- 手动 new
UserStatsService,传入 mock 的 Repository - 调用
$service->getActiveCount() - 用
$this->assertSame(2, $result)断言结果 - 若需验证 Repository 是否被调用一次,加
$mockRepo->expects($this->once())->method('findActiveUsers')
不推荐:在单元测试里连 SQLite 或 SchemaTool
那属于集成测试范畴。单元测试中启动内存数据库会破坏隔离性、拖慢执行、引入环境依赖:
- 不要调用
self::bootKernel()或static::createClient() - 不要使用
SqliteMemory连接或SchemaTool::createSchema() - 不加载任何 Doctrine 配置、不触发实体映射解析
- 如果真要验证 DQL 正确性,请另写集成测试,而非塞进单元测试
断言查询结果的关键点
你断言的不是“SQL 执行结果”,而是被测对象对查询结果的使用方式:
- 返回数组?用
$this->assertCount(3, $users)或$this->assertArrayHasKey('email', $users[0]) - 返回单个实体?用
$this->assertInstanceOf(User::class, $user) - 返回 null?用
$this->assertNull($result) - 若服务把查询结果转成 DTO,就断言 DTO 属性:
$this->assertSame('john@example.com', $dto->getEmail())


















