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

关于扩展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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 09:52:39