php数据访问层单元测试必须用mock对象切断真实数据库连接:1. 用createmock()生成空壳实例,禁用构造函数防止pdo连接;2. 用willreturn()或willreturnonconsecutivecalls预设查询返回值;3. 用expects()->with()->callback验证方法调用及参数;4. 用willthrowexception模拟异常路径。

要对PHP数据访问层(如Repository、DAO类)做可靠单元测试,必须切断真实数据库连接,用Mock对象模拟查询、插入、更新等行为,否则每次运行测试都会污染数据库或因网络/配置失败而中断。
创建Repository类的Mock实例
在测试类中调用$mockRepo = $this->createMock(UserRepository::class)生成空壳对象,它不执行任何真实数据库操作,所有方法默认返回null。
这一步不能跳过——直接new一个真实Repository会触发PDO连接,导致测试失败或写入脏数据。
【必须禁用__construct自动连接】若Repository构造函数内含数据库初始化逻辑,需用$this->getMockBuilder(UserRepository::class)->disableOriginalConstructor()->getMock()绕过。
预设查询方法返回值(Stub行为)
方法一:单次返回固定数组$mockRepo->method('findActiveUsers')->willReturn([['id'=>1,'name'=>'Alice'],['id'=>2,'name'=>'Bob']]);
方法二:按调用次数返回不同结果$mockRepo->expects($this->exactly(2))->method('findById')->willReturnOnConsecutiveCalls(['id'=>1,'email'=>'a@x.com'], null);
这能验证代码是否正确处理“查到”和“查不到”两种分支。
注意:若方法签名含类型提示(如public function findById(int $id): ?array),Mock返回null不会报错,但实际业务逻辑可能因未判空而崩,所以willReturnOnConsecutiveCalls比单纯willReturn(null)更贴近真实场景。
验证数据操作是否被正确调用
第一步:声明期望调用次数与参数$mockRepo->expects($this->once())->method('save')->with($this->callback(function($user) { return $user['status'] === 'pending' && isset($user['created_at']); }))
第二步:执行被测业务逻辑$service = new UserService($mockRepo); $service->register(['email'=>'test@x.com']);
第三步:PHPUnit自动校验save()是否被调用一次,且传入数组含status和created_at字段——不匹配则测试直接失败。
这个callback断言比with($this->equalTo([...]))更灵活,能忽略数组键顺序或额外字段,只聚焦关键业务约束。
模拟异常路径(数据库连接失败)
用willThrowException触发try-catch分支:$mockRepo->method('delete')->willThrowException(new PDOException('Connection lost'));
接着测试UserService的removeUser()方法是否捕获异常并返回false或抛出自定义业务异常。
这一步不可省略——真实项目里,90%的数据层故障发生在连接中断、超时、权限拒绝等非SQL逻辑错误上,仅测正常路径覆盖不全。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











