【问题标题】:why is it impossible to test a static method with mockery or anything else为什么不可能用嘲弄或其他任何东西来测试静态方法
【发布时间】:2018-11-21 11:17:39
【问题描述】:

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

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

1) 问题 1:我正在尝试理解 laravel 中的外观。正如我猜想的那样,它的实现是因为如果我们有类,并且它们有大的命名空间和大名称,并且每次我们想要使用这个类并且我们不想使用 new 关键字和 use 语句时,我们使用外观是一个更简单的代码和可读性。我还认为 laravel 实现了外观,因为他们想在他们的类中编写非静态函数以便可以对其进行测试。毕竟,我们使用像静态类这样的外观(因为可读性而不是使用 new 和 use),但实际上,它会创建新的实例。

我说的对吗?

2)如果以上是正确的,您能否提供一个示例,为什么不能像 laravel 文档所说的那样测试静态方法?

【问题讨论】:

    标签: php laravel


    【解决方案1】:

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

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

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

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

    如果您尚未将该类注册为单例,则外观基本上会为您处理。

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

    此外,通常不可能模拟或存根静态方法,但是如果您使用门面,则可以使用 \ABCFacade::swap($mockObject),因此可以模拟您的门面。

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

     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 中编码,那么如果您使用静态方法,那么您真的会受到限制。

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

    【讨论】:

    • 非常正确。在大多数语言中,您可以通过为该接口提供不同的实现来模拟 interface。 PHP 在这方面非常不严格,只要你有一个实现正确方法的对象实例,它通常会工作(除非有一些特定的类型检查正在进行)。但是在静态方法的情况下,根本没有对象实例。您直接引用某个类,并且确保您可以模拟该类的唯一方法是完全防止加载实际类并改为加载模拟版本。至少可以说是哈克。
    • laravel 本身说,它们提供了一种简洁易记的语法,允许您使用 Laravel 的功能,而无需记住必须手动注入或配置的长类名。而且我不认为它必须是单例才能使其工作
    • @NikaKhurashvili laravel Facades“缓存”请求中最后解析的实例,因此在实践中它表现为单例。但这并不意味着您需要将其设置为单例。
    • @apokryfos 它有什么理由缓存最后一个解析的实例并表现为单例吗?
    • @NikaKhurashvili 不完全确定为什么,但我猜如果你像静态一样使用它,那么如果它具有类似静态的行为是有意义的,即它在调用之间保持相同的状态。您可以随时调用\Facade::clearResolvedInstance(<name>) 来重置给定外观的状态
    猜你喜欢
    • 2017-09-01
    • 2012-07-20
    • 1970-01-01
    • 2019-02-20
    • 2021-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多