PHPUnit:在一次测试中对模拟方法使用多个断言是一种不好的做法吗?

标签 php unit-testing phpunit assertions

我正在测试一个旨在测试用户是否拥有给定电子邮件的对象。因此,在调用“tryEmail”方法时,它会向给定的电子邮件地址发送一 strip 有确认链接的消息。我的测试看起来像这样:

public function testSendingWasSuccessful() {

    $confirmationObject = $this->getMock('LT\EmailConfirmation\Model\ConfirmationObjectInterface');

    $testType = 'test.type';
    $testEmail = 'test@example.com';
    $testData = [];

    // EmailTester should create a new confirmation object.
    $this->manager->expects(static::once())
        ->method('create')->with($testType, $testEmail)
        ->willReturn($confirmationObject);

    // Then it should send the confirmation message.
    $this->mailer->expects(static::once())
        ->method('send')->with(static::identicalTo($confirmationObject))
        ->willReturn(true);

    // And save the confirmation object.
    $this->manager->expects(static::once())
        ->method('save')->with(static::identicalTo($confirmationObject));

    $tester = new EmailTester($this->repository, $this->manager, $this->confirmationHandler, $this->mailer);

    static::assertTrue($tester->tryEmail($testType, $testEmail, $testData));
}

现在您可以看到它可能存在的问题 - 它包含多个断言。为什么我决定在一个测试中使用这些断言?因为他们互相依赖。因此,只有在创建新的确认对象时才应发送确认消息,并且只有在发送确认消息时才应保存确认对象,最后,使用那些模拟方法的“tryEmail”方法的输出正在被断言。

但是,我觉得我不小心用我的断言描述了“tryEmail”方法的实现。但似乎需要完全覆盖此方法,并确保它始终按应有的方式工作。如果我删除任何这些断言,我可以想象错误会过去。例如:static::identicalTo($confirmationObject) 这基本上是:检查传递给邮件程序的对象是否与之前创建的对象相同。如果我要更改邮件程序的界面,我也必须更改 EmailTester 的这个测试,所以看起来我在这里做错了什么。然而与此同时——我如何在不引入这种耦合的情况下检查上述断言?或者也许我应该让这个未经测试?

我这样做是对还是错?我该如何改进它?什么时候真正在 mock 上使用断言?

补充:我只是有一个想法 - 测试类不是应该测试实现(如果实现符合接口(interface))吗?这意味着在测试中描述实现实际上是一件好事,因为它可以确保实现正常工作。这也意味着实现的耦合程度将转移到测试中,这是不可避免的。我错了吗?

最佳答案

“每个测试一个断言”的规则是让您的测试专注于被测试代码的一个特定行为。在测试中有多个断言并不是坏事。

使用模拟对象时,我更喜欢对被替换的方法进行某种断言。这样我就可以确保系统将按预期使用依赖项。

你的测试类是为了确认你的代码的行为。您拥有的断言可以是您手动执行的任何检查,以确保该类的行为符合您的预期。由于您希望以特定方式调用特定方法,因此您希望对它们进行断言。

我在测试中看到的问题是您有一个模拟对象返回一个模拟对象。这通常是一种代码味道,意味着您没有传递正确的依赖项。您可以将 LT\EmailConfirmation\Model\ConfirmationObjectInterface 对象的创建移出方法,并将其作为方法的依赖项传递。用此对象替换方法的前两个参数。

您似乎也没有在此测试中使用第三个参数,因此似乎没有必要。

关于PHPUnit:在一次测试中对模拟方法使用多个断言是一种不好的做法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38431640/

相关文章:

php - Mix list 不存在

php - 使用指向外部资源的 URL 在 Laravel 中下载文件

javascript - 发布到 Capsule CRM 和 "AJAX Proxy"

php - 在 symfony2 中对 css 和 js 文件进行逆向工程

python - 将 python mockito 与 python unittest 集成

php - 在 phpunit 中按完全匹配过滤

php - 为什么 PHPUnit 将一些右大括号显示为未被覆盖?

php - symfony 功能测试的 500 状态代码

ios - AdMob 只发送真实广告,而不发送测试广告,为什么?

javascript - 无论如何要对函数中定义的 javascript 函数进行单元测试?