【问题标题】:What happens to objects passed between dependent PHPUnit tests?在依赖 PHPUnit 测试之间传递的对象会发生什么?
【发布时间】:2011-07-25 19:53:39
【问题描述】:

这与其说是一个问题,不如说是为了节省我刚刚在 PHPUnit 上浪费的时间。

我的问题是我的模拟对象在依赖测试中使用时没有返回预期值。似乎 PHPUnit 并没有在依赖测试之间保留相同的对象,即使语法使它看起来像这样。

有谁知道为什么 PHPUnit 会这样做?这是一个错误吗? PHPUnit 中的类似内容让其使用起来非常令人沮丧。

<?php 
class PhpUnitTest
extends PHPUnit_Framework_TestCase
{
private $mock;

public function setUp()
{
    $this->mock = $this->getMock('stdClass', array('getFoo'));

    $this->mock->expects( $this->any() )
        ->method('getFoo')
        ->will( $this->returnValue( 'foo' ) );
}

public function testMockReturnValueTwice()
{
    $this->assertEquals('foo', $this->mock->getFoo());
    $this->assertEquals('foo', $this->mock->getFoo());

    return $this->mock;
}

/**
 * @depends testMockReturnValueTwice
 */
public function testMockReturnValueInDependentTest($mock)
{
    /* I would expect this next line to work, but it doesn't! */
    //$this->assertEquals('foo', $mock->getFoo());

    /* Instead, the $mock parameter is not the same object as
     * generated by the previous test! */
    $this->assertNull( $mock->getFoo() );
}

}

【问题讨论】:

  • 请添加命令行,当您遇到问题时如何调用 phpunit。 -- 你有什么理由让$mock 成为私人会员?
  • AFAIK phpunit 在每次测试之前运行 setUp() 方法,以便重置 $this->mock 的值
  • 我本以为你写下来的时候这会起作用,我认为setUp() 不会被调用@dependant 测试,所以我真的很惊讶这也失败了......那可能如果我有一个模拟传递给一个我用@depends 传递的类:)
  • @itom99: 是的,我原以为 $this->mock 会被重置,但请仔细查看代码:在 testMockReturnValueInDependentTest() 中,mock 对象作为局部变量传入。
  • @hakre:你想知道我为什么将 $this->mock 设为私有,或者为什么我将它设为实例变量(而不是本地变量)吗?

标签: php mocking phpunit depends


【解决方案1】:

PHPUnit 中的 Mock 对象附加到为其创建它们的测试实例,根据定义,这意味着单个测试方法。这样做的原因是 PHPUnit 允许您指定在测试期间必须满足的模拟期望。为此,一旦方法成功终止,它就会断言这些期望。如果模拟在测试中存在,期望就不会起作用。

问题在于它不支持存根对象:模拟只包含响应方法和输入而采取的预设动作。存根不会验证他们的方法是否像完整的模拟那样被调用。也许 PHPUnit 可以受益于在 setUpBeforeClass() 中创建与测试实例无关的存根的能力。

您的另一个选择是使用外部模拟对象库,例如 MockeryPhake

编辑:再次查看您的示例代码后,我想知道您为什么对这种行为感到惊讶。正如 Shaunak 所写,setUp() 在每个测试方法执行之前在一个新实例上被调用。因此,每个实例都会收到一个新的模拟 stdClass。如果您只希望一种测试方法接收期望,请将其添加到测试方法本身中。您仍然可以在 setUp() 中使用所有测试方法应该共有的任何行为来创建模拟对象。

【讨论】:

  • 所以换个说法:在每次测试之后,在该测试运行中创建的所有模拟对象都将被剥离所有分配的expectations?
  • @David:我感到困惑的原因是语法使它看起来像是被依赖者的返回值被传递给了依赖者。如果 PHPUnit 从头开始​​重建一切,为什么它首先提供这种误导性语法?
  • @edorian - 期望得到验证,但 AFAIK 并没有被剥夺。这发生在测试方法成功完成之后但在调用tearDown() 之前。虽然您可以在 tearDown() 中为模拟添加更多期望和调用,但这不会很有用。
  • @Ian - 啊,我没有看到你正在通过模拟到下一个测试。虽然它的一些期望可能会起作用——断言传递给with() 的参数值并执行will() 中指定的操作——调用计数期望将不会被验证,因为你传递给依赖方法的模拟没有附加到它的测试用例实例。您可以在模拟对象上调用__phpunit_verify(),但您正在如履薄冰。有关详细信息,请参阅PHPUnit_Framework_TestCase::verifyMockObjects():每个模拟都经过验证和清理。深入挖掘以了解这意味着什么。
  • @Ian - 依赖方法中注释掉的调用失败告诉我__phpunit_cleanup() 必须删除getFoo() 期望。
【解决方案2】:

我不是 php 人,如果我错了,请纠正我,但所有单元测试都设计为按以下顺序运行,

设置 --> 测试功能 --> 销毁。

所以每次在执行任何测试函数之前都会调用 setup 和 destroy 函数。这样做是为了保持单元测试的目的。

如果您想拥有相关的单元测试用例,那么您必须对它们进行编码,而不是依赖全局变量来做到这一点(这违背了单元测试的目的!)。如果有一个依赖于某个函数的测试用例“A”,则从“A”调用该函数,然后断言这些值。

【讨论】:

  • 销毁方法名为tearDown()
  • 没错!很抱歉我说的是笼统的说法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-07
  • 1970-01-01
  • 2016-02-06
  • 2020-05-16
  • 2017-11-24
  • 1970-01-01
  • 2013-01-16
相关资源
最近更新 更多