PHPUnit:何时使用模拟对象方法调用匹配器的最佳实践

PHPUnit: Best practice for when to use Mock Object method invocation matcher

我一直在在线阅读 PHPUnit 文档并一直在使用模拟对象匹配器,但我不确定您何时应该或不应该使用它们。对于下面的示例,假设我有一个 class Foo 我正在为以下内容编写测试:

富class

<?php

class Foo {
     public function isUserNamedTom(User $user):bool
     {
          return strtoupper($user->getName()) === 'TOM'; 
     }
}

我的测试

<?php

use PHPUnit\Framework\TestCase;

class FooTest extends TestCase {
     public function testIsUserNamedTom():void
     {
          $userMock = $this->getMockBuilder(User:class)
          ->disableOriginalConstructor()
          ->getMock();

          $userMock->expects($this->once())
          ->method('getName')
          ->willReturn('tom');

          $fooService = new Foo();
          $response = $fooService->isUserNamedTom($userMock);

          $this->assertTrue($response);
     }
}

我的问题是我应该在这个例子中使用 $this->once() 还是不使用,如果不是,那么原因是什么。

您的测试应该在尽可能少地了解被测方法的实现的情况下测试被测方法的输出。您希望测试在该方法不再执行它应该执行的操作时失败,但您不希望它在该方法以不同的方式执行正确的操作时失败。

在这种情况下,isUserNamedTom(User $user) 的测试似乎与 user->getName() 是否被调用一次、两次或从不调用无关。唯一重要的是 isUserNamedTom(User $user) returns true 当且仅当 $user 被命名为“tom”、“Tom”、“tOm”等

因此:不,我不会在这里检查$this->once()

我什至会尝试在不模拟的情况下通过并传递一个实例化的用户对象。但这是否可能取决于你的 User class.

 public function testIsUserNamedTom():void
 {
      $user = (new User())->setName('tom');
      $this->assertTrue($fooService->isUserNamedTom($user));
 }

基本上,在编写测试时,问问自己在什么情况下希望它们失败总是好的。在您的示例中,我的猜测是,如果有人重构 isUserNamedTom(User $user) 以便该方法不再调用 $user->getName()(但可能使用 public 属性 $user->name).