Lzh on GitHub

模拟硬依赖(`new` 关键字)

要模拟硬依赖,一个先决条件是我们正在尝试测试的代码使用自动加载。

要模拟硬依赖,一个先决条件是我们正在尝试测试的代码使用自动加载。

让我们以以下代码为例:

<?php
namespace App;

class Service
{
    function callExternalService($param)
    {
        $externalService = new Service\External($version = 5);
        $externalService->sendSomething($param);
        return $externalService->getSomething();
    }
}

在不更改代码本身的情况下测试此代码的方式是使用 overload 前缀创建 实例模拟

<?php
namespace AppTest;

use Mockery as m;

class ServiceTest extends \PHPUnit_Framework_TestCase
{
    public function testCallingExternalService()
    {
        $param = 'Testing';

        $externalMock = m::mock('overload:App\Service\External');
        $externalMock->shouldReceive('sendSomething')
            ->once()
            ->with($param);
        $externalMock->shouldReceive('getSomething')
            ->once()
            ->andReturn('Tested!');

        $service = new \App\Service();

        $result = $service->callExternalService($param);

        $this->assertSame('Tested!', $result);
    }
}

如果我们现在运行这个测试,它应该会通过。Mockery 完成了它的工作,我们的 App\Service 将使用模拟的外部服务而不是真实的那个。

问题在于,例如,当我们想测试 App\Service\External 本身,或者在我们的测试中其他地方使用了该类时。

当 Mockery 重载一个类时,由于 PHP 处理文件的方式,那个被重载的类文件不能被包含,否则 Mockery 会抛出 “类已存在” 异常。这就是自动加载发挥作用的地方,它使我们的工作变得容易得多。

为了实现这一点,我们将告诉 PHPUnit 在单独的进程中运行那些具有重载类的测试,并且不保留全局状态。这样我们就可以避免被重载的类被包含不止一次。当然,这也有其缺点,因为这些测试会运行得更慢。

我们上面的测试示例现在变为:

<?php
namespace AppTest;

use Mockery as m;

/**
 * @runTestsInSeparateProcesses
 * @preserveGlobalState disabled
 */
class ServiceTest extends \PHPUnit_Framework_TestCase
{
    public function testCallingExternalService()
    {
        $param = 'Testing';

        $externalMock = m::mock('overload:App\Service\External');
        $externalMock->shouldReceive('sendSomething')
            ->once()
            ->with($param);
        $externalMock->shouldReceive('getSomething')
            ->once()
            ->andReturn('Tested!');

        $service = new \App\Service();

        $result = $service->callExternalService($param);

        $this->assertSame('Tested!', $result);
    }
}

测试硬依赖的构造函数参数

有时我们可能希望确保硬依赖是用特定的参数实例化的。使用重载模拟,我们可以设置对构造函数的期望。

<?php
namespace AppTest;

use Mockery as m;

/**
 * @runTestsInSeparateProcesses
 * @preserveGlobalState disabled
 */
class ServiceTest extends \PHPUnit_Framework_TestCase
{
    public function testCallingExternalService()
    {
        $externalMock = m::mock('overload:App\Service\External');
        $externalMock->allows('sendSomething');
        $externalMock->shouldReceive('__construct')
            ->once()
            ->with(5);

        $service = new \App\Service();
        $result = $service->callExternalService($param);
    }
}
有关更直接且面向单进程测试的方式,请查看 类中模拟类
本菜谱条目改编自 Robert Basic 在其博客上发表的题为 使用 Mockery 模拟硬依赖 的博文。