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

时间:2017-04-28 11:14:24

标签: php unit-testing static composer-php mockery

我想为官方的Segment PHP集成单元测试一个包装类。因此,我必须使用Mockery模拟the Segment class,因此无法获得任何真正的API请求。

问题

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

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

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

Could not load mock Segment, class already exists.

(这是有道理的,因为文档说明之前不能加载别名类。)

问题

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

1 个答案:

答案 0 :(得分:2)

基本上,你不能用静态调用来模拟类。

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

执行不同代码的唯一方法是不包含原始类,而是首先加载mock类代码。如果您有替代代码文件,或者使用Mockery调用eval(),则无关紧要。这两种方式都有效。

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

解决方案,也在评论中提到:没有静态方法的类。始终创建类的实例并调用动态方法。这样你就可以轻松地模拟类,但它需要先创建一个实例,并提供一种方法将类(或至少是模拟的类)注入到你想要测试的代码中。

作为一种通用模式,如果项目中没有依赖注入,我会使用它(我有时需要处理一些遗留的东西):

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

这样我就不必注入类了,但是我可以注入一个mock而不是真正的类。

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

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

如果您的依赖注入框架能够进行自动连接(如PHP-DI),并且您只有一个MyClass,那么这很有效,因为这将自动注入而无需您定义任何内容。 / p>