为什么不可能用嘲讽或其他方法测试静态方法

时间:2018-11-21 11:17:40

标签: php laravel

我已经在laravel的外观文档中阅读了以下句子:

  

通常,不可能模拟或存根真正的静态   类方法。

1)问题1:我试图了解laravel中的外观。我猜想,之所以实现它是因为,如果我们有类,并且它们具有很大的名称空间和名字,并且每次我们想要使用此类并且我们不想使用new关键字和use语句时,我们使用的外观是更简单的代码和可读性。我还认为laravel实现了Facade,因为他们想在类中编写非静态函数,以便对其进行测试。在所有这些之后,我们使用类似于静态类的外观(因为具有可读性,而不是使用new和use),但实际上,它创建了新的实例。

我对吗?

2)如果以上都是正确的,您能举个例子说明为什么无法像laravel docs所说的那样测试静态方法吗?

1 个答案:

答案 0 :(得分:2)

外观不能解决您提到的大型名称空间问题。大名称空间使用别名解决。您可以在config/app.php中声明它们,而Laravel在调用它们时会在内部使用class_alias。这就是例如\Cache\DB工作。

Facade基本上是另一个类的单例对象实例的代理类(Facade本身确保实例是单例)。

通常要在Laravel中注册单身人士:

  1. 在您的服务提供商中添加app()->singleton(ABC::class)
  2. 通过app()->make(ABC::class)->...
  3. 访问它

如果您尚未将该类注册为单身人士,则立面基本上可以为您解决此问题。

基本上,外观是一种代理另一个类的单例实例的方法。

通常也无法模拟或存根静态方法,但是,如果您使用的是Facade,则可以执行\ABCFacade::swap($mockObject),因此可以模拟Facade。

不能测试静态方法也是错误的。您可以绝对测试静态方法。例如:

 public testStaticMethod() {
      $this->assertEquals(1, ABC::method()); // We tested a static method against a desired behaviour
 }

您通常不能做的是模拟静态方法。通常,这是使用PHPUnit模拟事物的方式:

public testWithDependency() { 
      $dependency = $this->getMockBuilder(Dependency::class)->getMock();
      $dependency->expects($this->once())->method('dependantMethod')->willReturn(true);
      $objectToTest = new ABC($dependency); //We're passing a fake dependency which behaves in an ideal way
      $this->assertEquals(1, $objectToTest->methodToTest()); //Any calls to the dependency will call mock methods and not real ones
 }

尝试模拟静态方法时出现问题。如您所见,模拟会创建特定类型的模拟 instances 。它不能模拟该类型的静态成员,因为模拟对象本身实际上不是该类型。

但是,正如我刚刚发现的那样,不可能模拟或存根静态方法的说法并不完全正确。您可以AspectMock模拟静态方法或辅助方法。这似乎可以通过自定义自动加载器拦截所有函数调用而起作用。

话虽这么说,只是因为您不能代表使用静态方法是一种好习惯,还需要考虑其他问题,例如您通常无法在大多数编程语言中使用静态接口,或者通常不能在大多数编程语言中覆盖静态方法。请注意此处的“大多数编程语言”部分。在PHP中,完全可以使用late static binding覆盖静态方法,但这意味着在实现静态方法时需要对此做出明智的决定。

另一个缺点是,一类静态对象无法实现接口,因为接口适用于对象行为,而不适用于静态行为。因此,如果您使用的是静态操作,则不能将一个接口换成另一个接口。

通常,对静态方法的厌恶不是由于可测试性,而是因为如果您使用OOP进行编码,则使用静态方法的确受到限制。

希望这将有助于消除一些困惑。