关于扩展Mockery断言以适配常见使用场景的技术问询
扩展Mockery断言以适配常见使用场景的技术问询
当然可以!扩展Mockery来简化这类重复的断言逻辑完全可行,而且用Mockery自带的宏(Macro)功能来实现会比你之前尝试的TestCase扩展方案更优雅、更灵活。
核心思路:用Mockery宏封装通用断言逻辑
Mockery本身支持通过宏来扩展自身的静态方法,这样你就能直接在Mockery类上调用自定义的断言方法,比如Mockery::onUser($user),完全贴合Mockery原生的使用习惯,不用依赖TestCase或者Trait。
具体实现步骤
你可以在测试启动阶段(比如TestCase.php的setUp方法里)注册这些宏,这样所有测试用例都能直接使用:
public function setUp(): void { parent::setUp(); // 第一步:注册通用的「实例+属性匹配」宏 // 这个宏可以复用在任何类上(模型、DTO、自定义服务类都支持) Mockery::macro('onInstanceOfWithAttributes', function (string $className, array $attributes) { return Mockery::on(function ($arg) use ($className, $attributes) { // 先检查实例类型是否匹配 if (!$arg instanceof $className) { return false; } // 逐一检查属性是否匹配 foreach ($attributes as $key => $value) { if ($arg->{$key} !== $value) { return false; } } return true; }); }); // 第二步:封装针对User模型的快捷宏 // 直接复用上面的通用宏,简化调用 Mockery::macro('onUser', function (\App\Models\User $user) { return Mockery::onInstanceOfWithAttributes(\App\Models\User::class, ['id' => $user->id]); }); // 第三步:封装支持多参数的User角色匹配宏 Mockery::macro('onUserOfRole', function (int $userId, string $role) { return Mockery::onInstanceOfWithAttributes(\App\Models\User::class, [ 'id' => $userId, 'role' => $role ]); }); // 额外:如果需要支持非模型类(比如自定义DTO),直接复用通用宏即可 // Mockery::macro('onOrderDto', function (array $attributes) { // return Mockery::onInstanceOfWithAttributes(\App\DTOs\OrderDto::class, $attributes); // }); }
为什么这个方案更好?
- 更贴合Mockery的API风格:直接通过
Mockery::xxx()调用,不用依赖$this->xxx(),和原生的Mockery::on()、Mockery::any()等方法保持一致。 - 扩展性极强:通用宏
onInstanceOfWithAttributes可以适配任何类,不管是Laravel模型还是自定义的业务类,解决了你之前方案只支持模型的局限。 - 代码更简洁:原本冗长的闭包断言,现在只需要一行
Mockery::onUser($user)就能搞定。
优化:添加清晰的错误提示(可选)
如果想在断言失败时获得更具体的调试信息,可以修改通用宏的闭包,抛出带详细信息的异常:
Mockery::macro('onInstanceOfWithAttributes', function (string $className, array $attributes) { return Mockery::on(function ($arg) use ($className, $attributes) { if (!$arg instanceof $className) { throw new \Mockery\Exception\InvalidArgumentException( sprintf('Expected instance of %s, got %s', $className, get_class($arg)) ); } foreach ($attributes as $key => $value) { if ($arg->{$key} !== $value) { throw new \Mockery\Exception\InvalidArgumentException( sprintf('Attribute "%s" mismatch: expected "%s", got "%s"', $key, $value, $arg->{$key}) ); } } return true; }); });
这样当断言失败时,你会得到明确的错误信息,比如“Expected instance of App\Models\User, got stdClass”或者“Attribute "role" mismatch: expected "admin", got "editor"”,大大提升调试效率。
总结
这个方案完全满足你的需求:
- 简化了重复的断言代码,实现
Mockery::onUser($user)这类简洁调用 - 支持传递多个参数,比如
Mockery::onUserOfRole($userId, $role) - 兼容所有类类型,不再局限于Laravel模型
- 比绑定在TestCase上的方案更优雅、更符合Mockery的设计逻辑
备注:内容来源于stack exchange,提问作者Tyler Wall
相关产品推荐
相关产品推荐

