Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/266.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PHPUnit:在一个测试中对模拟方法使用多个断言是一种不好的做法吗?_Php_Unit Testing_Phpunit_Assertions - Fatal编程技术网

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

PHPUnit:在一个测试中对模拟方法使用多个断言是一种不好的做法吗?,php,unit-testing,phpunit,assertions,Php,Unit Testing,Phpunit,Assertions,我正在测试一个对象,该对象旨在测试用户是否拥有给定的电子邮件。因此,在调用“tryEmail”方法时,它会向给定的电子邮件地址发送一条带有确认链接的消息。我的测试如下所示: public function testSendingWasSuccessful() { $confirmationObject = $this->getMock('LT\EmailConfirmation\Model\ConfirmationObjectInterface'); $testType

我正在测试一个对象,该对象旨在测试用户是否拥有给定的电子邮件。因此,在调用“tryEmail”方法时,它会向给定的电子邮件地址发送一条带有确认链接的消息。我的测试如下所示:

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”方法的实现。但似乎需要对这种方法进行全面覆盖,并确保它始终按其应有的方式工作。我可以想象,如果我删除这些断言中的任何一个,bug就会过去。例如:
static::identicalTo($confirmationObject)
基本上是:
检查传递给邮件程序的对象是否与之前创建的对象相同。如果我要更改邮件程序的界面,我还必须更改
EmailTester
的这个测试,所以看起来我在这里做错了什么。然而,与此同时,我如何在不引入这种耦合的情况下检查上述断言?或者我应该不去测试

我做得对还是错?我怎样才能改进它?什么时候在mock上使用断言真的

添加:我刚刚有一个想法-测试类不是应该测试实现的吗(如果实现符合接口)?这意味着在测试中描述实现实际上是一件好事,因为它可以确保实现正常工作。这也意味着实现的耦合级别将继续进行测试,这是不可避免的。我错了吗?

每个测试一个断言的规则是让测试集中在被测试代码的一个特定行为上。在一个测试中有多个断言并不是一件坏事

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

测试类用于确认代码的行为。您拥有的断言将是手动执行的任何检查,以确保类的行为符合您的预期。因为您希望以特定的方式调用特定的方法,所以需要为它们提供一个断言

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


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

谢谢您的回答!你对
LT\EmailConfirmation\Model\ConfirmationObjectInterface
的评论让我思考了一下。我有意识地这样设计它,以尽量减少使用该对象时所需的工作量。现在我几乎可以在控制器中使用一个函数
tryEmail
,就可以完成了。否则,我将不得不拉动管理器,在控制器中创建确认对象,然后通过
tryEmail
方法传递它,这将是控制器中的更多代码,这似乎是个坏主意。你确定会有进步吗?我很有信心。我为您的对象增加了灵活性,
EmailTester
现在可以处理不同类型的对象。这也让他们集中精力。如果您需要发送不同的电子邮件类型,则此对象应该能够毫无问题地处理它。