我已经在laravel的外观文档中阅读了以下句子:
通常,不可能模拟或存根真正的静态 类方法。
1)问题1:我试图了解laravel中的外观。我猜想,之所以实现它是因为,如果我们有类,并且它们具有很大的名称空间和名字,并且每次我们想要使用此类并且我们不想使用new关键字和use语句时,我们使用的外观是更简单的代码和可读性。我还认为laravel实现了Facade,因为他们想在类中编写非静态函数,以便对其进行测试。在所有这些之后,我们使用类似于静态类的外观(因为具有可读性,而不是使用new和use),但实际上,它创建了新的实例。
我对吗?
2)如果以上都是正确的,您能举个例子说明为什么无法像laravel docs所说的那样测试静态方法吗?
答案 0 :(得分:2)
外观不能解决您提到的大型名称空间问题。大名称空间使用别名解决。您可以在config/app.php
中声明它们,而Laravel在调用它们时会在内部使用class_alias
。这就是例如\Cache
或\DB
工作。
Facade基本上是另一个类的单例对象实例的代理类(Facade本身确保实例是单例)。
通常要在Laravel中注册单身人士:
app()->singleton(ABC::class)
app()->make(ABC::class)->...
如果您尚未将该类注册为单身人士,则立面基本上可以为您解决此问题。
基本上,外观是一种代理另一个类的单例实例的方法。
通常也无法模拟或存根静态方法,但是,如果您使用的是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进行编码,则使用静态方法的确受到限制。
希望这将有助于消除一些困惑。