代码之家  ›  专栏  ›  技术社区  ›  gcb

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

  •  1
  • gcb  · 技术社区  · 9 年前

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

    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 回复  |  直到 9 年前
        1
  •  4
  •   Schleis    9 年前

    您的Logger类不能被PHPUnit嘲笑,原因有两个。

    1. 该类列为 final 这意味着它不能被扩展。当PHPUnit创建对象的模拟时,它会创建一个新的对象来扩展被模拟的类。这个 最终的 关键字阻止扩展类,因此无法创建mock。
    2. 静态调用要替换的方法。您无法替换它们,因为调用被定向到实际的类。 PHPUnit 5.3 has a way of "mocking" static methods but that is only within the class being called statically. 它不会替换来自类外的调用。

    http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/

    您有两个选项来测试这样的事情:

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

    如果您使用的是像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());
        }
    }
    ?>