Extend Mockery::关于常见用例的断言

问题描述 投票:0回答:1

是否可以为常用断言扩展Mockery

断言一个模拟函数接收一个类的实例并匹配一个标识符是很麻烦的。

Mockery::on(
  fn ($arg) => $arg instanceof App\Models\User && $arg->id === $user->id
)

有没有可能...

  1. 通过扩展 Mockery(通过宏?)简化此调用,以便此断言可以是

    Mockery::onUser($user)
    Mockery::onUserOfId($id)
    ?

  2. 传入多个参数,如

    Mockery::onUserOfRole($id, $user->role)
    ?

注意:在测试中提供给

$user
调用的
Mockery::on
对象实例 not 与传递给被测试的模拟函数的实例相同,因此检查类型并匹配标识符。

可能的解决方案......那是不灵活的

我们探索过的一种可能的解决方案是使用

TestCase
函数扩展
matchModel()
基类,但这种扩展并不“优雅”。我考虑过把它分解成一个 Trait,但是把这些函数附加到所有的 Test Cases 上,
$this->onUserOfRole()
似乎是错误的。该解决方案也失败或需要定制。 (
Illuminate\Database\Eloquent\Model@is()
有效,但是......)如果对象不是 Laravel 模型,那么我们回到原点:

# Possible Solution that is not extensible and only works on Models
class TestCase extends Illuminate\Foundation\Testing\TestCase
    protected function matchModel(Model $model)
    {
        return new MockeryClosure(function ($arg) use ($model) {
            if ($model->is($arg)) {
                return true;
            }

            // print custom error message

            return false;
        });
    }
}
php laravel mocking mockery
1个回答
0
投票

Mockery::on 方法只是

\Mockery\Matcher\Closure
的构造函数的单行包装器,它反过来只定义了两个自定义逻辑:

  • a
    match
    方法接受被检查的参数,并返回 true 或 false
  • a
    __toString
    方法返回固定字符串
    '<Closure===true>'
    ,大概用于错误/调试消息

实际的闭包没有直接定义为一个属性,它只是依赖于继承自

MatcherAbstract
的构造函数,它设置了一个无类型的属性
$_expected
,但不直接执行其他逻辑。

因此,您的用例的替代品可以简单地用您想要的任何参数覆盖该构造函数,并在

match
方法中使用它们来实现您的测试,例如

class MyMatcher extends Mockery\Matcher\MatcherAbstract
{
    private string $expectedClass;
    private string $keyName;
    private mixed $keyValue;

    public function __construct(string $expectedClass, string $keyName, mixed $keyValue)
    {
        $this->expectedClass = $expectedClass;
        $this->keyName = $keyName;
        $this->keyValue = $keyValue;
    }

    /**
     * Check if the actual value matches the expected.
     *
     * @param mixed $actual
     * @return bool
     */
    public function match(&$actual)
    {
        return
            is_a($actual, $this->expectedClass, true)
            && 
            $actual->{$this->keyProperty} === $this->keyValue;
    }

    /**
     * Return a string representation of this Matcher
     *
     * @return string
     */
    public function __toString()
    {
        return "<Is {$this->expectedClass} and has {$this->keyProperty} of {$this->keyValue}>";
    }
}

然后这个:

Mockery::on(
  fn ($arg) => $arg instanceof App\Models\User && $arg->id === $user->id
)

变成这样:

new MyMatcher(App\Models\User::class, 'id', $user->id)

如果你想进一步缩短

new MyMatcher
,你可以在任何你喜欢的地方放置一个函数,将它包装在相同的
Mockery::on
包装
new \Mockery\Matcher\Closure
.

如果你想要更复杂或可配置的测试,你需要弄清楚的是一种从

match
返回布尔结果的方法。

© www.soinside.com 2019 - 2024. All rights reserved.