PHP 单元测试 - 使用 Mockery 自动加载模拟静态 class

PHP Unit Testing - Mock static autoloaded class with Mockery

我想对官方 Segment PHP 集成的包装器 class 进行单元测试。因此,我将不得不使用 Mockery 模拟 the Segment class,这样就不会有任何真正的 API 请求。

问题

要模拟的 class 仅包含静态方法。因此,我尝试像这样模拟它(使用 alias):

$segment = Mockery::mock('alias:Segment');

这有效,但前提是 class 没有被作曲家自动加载。如果我加载它 - 就像我必须为应用程序的其余部分一样 - 我会收到错误

Could not load mock Segment, class already exists.

(这是有道理的,因为文档声明之前不得加载别名 class。)

问题

我怎样才能嘲笑这个(邪恶的?)class,但仍然像往常一样在我的应用程序的其余部分中使用它?

本质上,您不能使用静态调用模拟 classes。

静态调用始终准确引用 class 和要调用的方法,这相当于准确指向要执行的文件和代码行(如果您假设基本的自动加载功能可用)。

执行不同代码的唯一方法是不包含原始 class,而是首先加载模拟 class 代码。如果您有替代代码文件,或者您是否使用 Mockery 调用 eval(),都没有关系。两种方式都可以。

但它们也只能工作一次。您不能在以后的测试中切换回原始代码,因为每个脚本 运行 只能定义一次 class。无法切换实现(如原始与模拟与另一个模拟)是这里的问题。

解决方案,评论中也提到了:Don't have classes with static methods。始终创建 classes 的实例并调用动态方法。通过这种方式,您可以轻松模拟 class,但它需要先创建一个实例,并提供一种将 class(或至少模拟的 class)注入代码的方法想测试一下。

如果依赖注入在项目中不可用,我将使用它作为通用模式(有时我必须处理一些遗留问题):

public function __construct(MyClass $class = null) {
    $this->class = $class ?: new MyClass();
}

这样我就不必注入 class,但我可以注入模拟而不是真实的 class。

对于依赖注入可用的情况,构造函数将是一个非常基本的初始化器:

public function __construct(MyClass $class) {
    $this->class = $class;
}

如果您的依赖注入框架能够进行自动连接(如 PHP-DI),并且您只有一个 MyClass,这将非常有用,因为它会在没有您的情况下自动注入必须定义任何东西。