【问题标题】:Is there an equivalent of SimpleTest's "partial mocks" in PHPUnit?PHPUnit 中是否有相当于 SimpleTest 的“部分模拟”?
【发布时间】:2010-11-12 22:43:07
【问题描述】:

我正在尝试将一堆测试从 SimpleTest 迁移到 PHPUnit,我想知道是否有与 SimpleTest 的 partial mocks 等效的测试。

我似乎在文档中找不到任何表明此功能可用的内容,但我突然想到我可以只使用一个子类。这是个好主意还是坏主意?

class StuffDoer {
    protected function doesLongRunningThing() {
        sleep(10);
        return "stuff";
    }
    public function doStuff() {
        return $this->doesLongRunningThing();
    }
}

class StuffDoerTest {
    protected function doesLongRunningThing() {
        return "test stuff";
    }
}

class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
    public function testStuffDoer() {
        $sd = new StuffDoerTest();
        $result = $sd->doStuff();
        $this->assertEquals($result, "test stuff");
    }
}

【问题讨论】:

    标签: php phpunit simpletest


    【解决方案1】:

    通过阅读链接页面,SimpleTest 部分模拟似乎是一个模拟,其中只有一些方法被覆盖。如果这是正确的,则该功能由普通的 PHPUnit 模拟处理。

    PHPUnit_Framework_TestCase 中,您可以创建一个模拟

    $mock = $this->getMock('Class_To_Mock');
    

    它创建了一个模拟实例,其中所有方法都不执行任何操作并返回 null。如果你只想重写部分方法,getMock 的第二个参数是要重写的方法数组。

    $mock = $this->getMock('Class_To_Mock', array('insert', 'update'));
    

    将创建一个 Class_To_Mock 的模拟实例,其中删除了 insertupdate 函数,准备好指定它们的返回值。

    此信息位于phpunit docs

    注意this answer 显示了更多最新的代码示例,适用于 PHPUnit 5.4 开始的版本

    【讨论】:

      【解决方案2】:

      我认为 PHPUnit 不支持对被测系统进行部分模拟。如果您尝试隔离方法,那么我确信您的实现有效 - 我也这样做了。

      但是,出于几个原因,我尽量避免这样做。

      首先,它将您的测试与类的内部实现紧密结合。您真的关心是否调用了名为doesLongRunningThing 的方法,还是“LongRunningThing”完成更重要?

      其次,当我遇到这个问题时,我总是想知道我是否让一个班级完成了两个班级的工作。 extract class 重构可能是有序的。如果doesLongRunningThing() 成为自己的类,即使使用单个方法,测试也会变得容易得多。

      我相信解决方案是注入您的 SUT 所依赖的服务 (http://en.wikipedia.org/wiki/Dependency_injection)。这也使DoesLongRunningThing 实现更易于测试。

      不跳入界面,我会这样做:

      class DoesLongRunningThing {
          public function execute() {
              sleep(10);
              return "stuff";
          }
      }
      
      class StuffDoer {
          protected $doesLongRunningThing;
      
          public function setLongRunningThinger(DoesLongRunningThing $obj) {
              $this->doesLongRunningThing = $obj;
          }
      
          public function doStuff() {
              return $this->doesLongRunningThing->execute();
          }
      }
      

      现在很容易模拟:

      class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
          public function testStuffDoer() {
              $dlrtMock = $this->getMock('DoesLongRunningThing');
              $dlrtMock->expects($this->any())->will($this->returnValue("test stuff"));
      
              $sd = new StuffDoer();
              $sd->setLongRunningThinger($dlrtMock);
              $result = $sd->doStuff();
              $this->assertEquals($result, "test stuff");
          }
      }
      

      【讨论】:

      • 我认为 PHPUnit 只是一个工具,它并不关心被测系统(这是一个工具用户的交易)。根据 Brenton 的回答,PHPUnit supports “部分模拟”。从第一句话开始,你的答案是完美的(+1)。
      【解决方案3】:

      PHPUnit_Framework_TestCase::getMock is deprecated since phpunit 5.4。我们可以改用setMethods

      setMethods(array $methods) 可以在 Mock Builder 对象上调用,以指定要替换为可配置测试替身的方法。其他方法的行为没有改变。如果调用 setMethods(null),则不会替换任何方法。

      https://phpunit.de/manual/current/en/test-doubles.html

      $observer = $this->getMockBuilder(Observer::class)
                       ->setMethods(['update'])
                       ->getMock();
      

      请注意,上面的getMockPHPUnit_Framework_MockObject_MockBuilder::getMock。 (phpunit5.6)

      【讨论】:

      • setMethods() 也被贬低了
      • setMethods 在 PHPUnit 8.x 中已被弃用,您现在可以使用onlyMethods,更多信息here
      【解决方案4】:

      可能是您正在模拟的课程不在范围内(我遇到了这个问题)。解决后,我能够模拟一个函数并测试另一个函数的实际逻辑。

      $mockController = $this->getMockBuilder('ControllerClassName')
                             ->setMethods(['functionToMock'])
                             ->getMock();
      
      $result = $mockController->anotherFunction();
      $this->assertEquals(true, $result);
      

      【讨论】:

        【解决方案5】:

        setMethods 方法已被弃用。现在可以了:

        $mock = $this->getMockBuilder(ClassToMock::class)
            ->onlyMethods(['insert', 'update'])
            ->getMock();
        

        【讨论】:

          猜你喜欢
          • 2010-09-07
          • 2013-01-17
          • 1970-01-01
          • 2010-10-01
          • 1970-01-01
          • 2011-02-11
          • 1970-01-01
          • 2014-03-26
          • 2012-08-02
          相关资源
          最近更新 更多