模拟全局静态方法的最佳方法

时间:2015-03-06 20:59:50

标签: php unit-testing testing phpunit

我的应用程序有一个带有签名的记录器库:

final class Logger {
  public static method debug($msg);
  public static method warn($msg);
  public static method error($msg);
}

我要测试的类,另一个全局静态助手,将其用作

final class TestMe {
  public static method TestThis(){
    Logger::debug('TestThis() called');
    doThings();
  }
}

如何通过模拟TestMe并等待预期的消息来测试Logger类?

2 个答案:

答案 0 :(得分:2)

由于几个原因,PHPUnit无法模拟您的Logger类。

  1. 该类列为final,表示无法扩展。当PHPUnit创建对象的模拟时,它会创建一个新对象来扩展被模拟的类。 final关键字阻止扩展类,因此无法创建模拟。
  2. 要静态调用要替换的方法。您无法替换它们,因为调用是针对实际的类。 PHPUnit 5.3 has a way of "mocking" static methods but that is only within the class being called statically.它不会取代来自课外的电话。
  3. http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/

    您有两种方法可以测试这样的事情:

    1. 增加您正在测试的行为的范围。断言无论Logger类正在做什么就完成了。这将使任何测试不再是“单元”测试,但它们会封装预期的行为并且仍然有用。
    2. 重构您的用法以使用依赖注入,以便您可以传入不会静态调用方法的mockLogger。这可能会更痛苦,但会使您的代码更灵活IMO。

答案 1 :(得分:-2)

如果您正在使用像PHPUnit这样的测试框架,它可以模拟对象。您可以为记录器类创建一个模拟对象,并在其中定义调试方法。

这里详细解释:

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

以下是该页面的一个小例子:

<?php
require_once 'SomeClass.php';

class StubTest extends PHPUnit_Framework_TestCase
{
    public function testStub()
    {
        // Create a stub for the SomeClass class.
        $stub = $this->getMockBuilder('SomeClass')
                     ->getMock();

        // Configure the stub.
        $stub->method('doSomething')
             ->willReturn('foo');

        // Calling $stub->doSomething() will now return
        // 'foo'.
        $this->assertEquals('foo', $stub->doSomething());
    }
}
?>