PHPUnit方法调用期望与断言

时间:2016-06-04 10:48:55

标签: unit-testing phpunit mockery

创建模拟类通常涉及在模拟/测试双精度上配置方法调用期望。

E.g。在'vanilla'PHPUnit中,我们可以存根方法调用并设置这样的期望:

$stub->expects($this->any())->method('doSomething')->willReturn('foo');

Mockery模拟对象框架中,我们得到这样的API:

$mock->shouldReceive('doIt')->with(m::anyOf('this','that'))->andReturn($this->getSomething());

这些期望通常在测试套件的设置阶段连线,例如, setUp()的{​​{1}}方法。

如果没有达到目标,那么上述期望会破坏测试。因此,使期望成为实际的断言。

这导致我们在测试用例类中散布断言(断言+期望)的情况,因为我们最终在测试用例的设置阶段以及单个测试中都有实际的断言。

在“常规”\PHPUnit_Framework_TestCase方法中测试方法调用期望是不错的做法。这可能看起来像那样(Mockery):

assert..

以及后来的一种测试方法的结尾:

public function setUp()
{
    $mock = m::mock(SomeClass::class);
    $mock->shouldReceive('setSomeValue');
    $this->mock = $mock;
}

public function testSoemthing() { ... $this->assertMethodCalled($this->mock, 'setSomeValue'); } 不是PHPUnit公开的方法。它必须实施。

简而言之,我们是否应该将期望声明视为实际断言,并在我们的测试方法中对它们进行测试?

1 个答案:

答案 0 :(得分:2)

您不需要在setUp()方法中配置测试双打(存根/模拟)。

实际上,我绝不会在测试设置代码中配置模拟,我只会在其中放置非常常见的存根。对于每个测试用例,模拟期望通常是不同的。我宁愿在设置代码中实例化我的测试双打,将它们分配给私有属性并在每个测试用例中配置它们。

private $registration;
private $repository;

protected function setUp()
{
    $this->repository = $this->getMock(UserRepository::class);
    $this->registration = new Registration($repository);
}

public function testUserIsAddedToTheRepositoryDuringRegistration()
{
    $user = $this->createUser();

    $this->repository
         ->expects($this->once())
         ->method('add')
         ->with($user);

    $this->registration->register($user);
}

将测试双配置放在测试用例中是很好的。它实际上更好,因为您的测试用例具有所有上下文,因此更具可读性。如果您发现自己配置了大量测试双打或编写长测试用例,这可能意味着您有太多的协作者,您应该考虑如何改进您的设计。您还可以使用具有有意义名称的辅助方法来使您的测试用例更具可读性 - 即$this->givenExistingUser()$this->givenRepositoryWithNoUsers()

正如您所注意到的,模拟期望(不要将它们与不符合预期的存根混淆)非常接近断言。实际上有一种方法可以用另一种类型的测试双打来实现你正在寻找的东西 - 间谍。

经典的phpunit模拟框架不支持间谍。但是,PHPUnit现在已经内置了对预言的支持。幸运的是,预言supports spies。这是一个例子:

private $registration;
private $repository;

protected function setUp()
{
    $this->repository = $this->prophesize(UserRepository::class);
    $this->registration = new Registration($repository->reveal());
}

public function testUserIsAddedToTheRepositoryDuringRegistration()
{
    $user = $this->createUser();

    $this->registration->register($user);

    $this->repository->add($user)->shouldHaveBeenCalled();
}

最后一点,我在同一个测试用例中避免断言和模拟期望。大多数情况下,这些是独立的行为,应该用不同的测试用例来涵盖。

要了解有关测试双打的详情,请阅读优秀的PHP Test Doubles Patterns