类常量
当为一个类创建测试替身时,Mockery 不会为我们正在模拟的类中定义的任何类常量创建存根。然而,有时这些类常量的缺失,加上测试设置和应用程序代码本身,可能会导致不期望的行为,甚至一个 PHP 错误:PHP Fatal error: Uncaught Error: Undefined class constant 'FOO' in ...
虽然在 Mockery 中支持类常量是可能的,但这需要大量的工作,而用例却很少。
命名模拟
然而,我们可以用 Mockery 支持的方式来处理这些常量——通过使用 命名模拟(Named Mocks)。
命名模拟是一个测试替身,它拥有我们想要模拟的类的名称,但在其之下是一个模拟真实类的存根类,带有预设的响应。
让我们看一个虚构但并非不可能的场景:
class Fetcher
{
const SUCCESS = 0;
const FAILURE = 1;
public static function fetch()
{
// Fetcher gets something for us from somewhere...
return self::SUCCESS;
}
}
class MyClass
{
public function doFetching()
{
$response = Fetcher::fetch();
if ($response == Fetcher::SUCCESS) {
echo "Thanks!" . PHP_EOL;
} else {
echo "Try again!" . PHP_EOL;
}
}
}
我们的 MyClass 调用 Fetcher,Fetcher 从某个地方获取一些资源——也许它从远程 Web 服务下载一个文件。我们的 MyClass 根据 Fetcher::fetch() 调用的响应打印出一条响应消息。
在测试 MyClass 时,我们并不希望 Fetcher 在我们每次运行测试套件时都去互联网上下载随机的东西。所以我们把它模拟出来:
// 使用别名:因为 fetch 是静态调用的!
\Mockery::mock('alias:Fetcher')
->shouldReceive('fetch')
->andReturn(0);
$myClass = new MyClass();
$myClass->doFetching();
如果我们运行这段代码,我们的测试会因为一个恼人的 PHP Fatal error: Uncaught Error: Undefined class constant 'SUCCESS' in ... 而报错。
这就是 namedMock() 在这种情况下如何帮助我们的。
我们为 Fetcher 类创建一个存根,为类常量创建存根,然后使用 namedMock() 创建一个基于我们存根的名为 Fetcher 的模拟:
class FetcherStub
{
const SUCCESS = 0;
const FAILURE = 1;
}
\Mockery::namedMock('Fetcher', 'FetcherStub')
->shouldReceive('fetch')
->andReturn(0);
$myClass = new MyClass();
$myClass->doFetching();
这之所以有效,是因为在底层,Mockery 创建了一个名为 Fetcher 的类,它扩展了 FetcherStub。
即使 Fetcher::fetch() 不是一个静态依赖,同样的方法也有效:
class Fetcher
{
const SUCCESS = 0;
const FAILURE = 1;
public function fetch()
{
// Fetcher gets something for us from somewhere...
return self::SUCCESS;
}
}
class MyClass
{
public function doFetching($fetcher)
{
$response = $fetcher->fetch();
if ($response == Fetcher::SUCCESS) {
echo "Thanks!" . PHP_EOL;
} else {
echo "Try again!" . PHP_EOL;
}
}
}
而测试将会有类似这样的代码:
class FetcherStub
{
const SUCCESS = 0;
const FAILURE = 1;
}
$mock = \Mockery::mock('Fetcher', 'FetcherStub');
$mock->shouldReceive('fetch')
->andReturn(0);
$myClass = new MyClass();
$myClass->doFetching($mock);
常量映射
另一种模拟类常量的方法是使用常量映射配置。
给定一个带有常量的类:
class Fetcher
{
const SUCCESS = 0;
const FAILURE = 1;
public function fetch()
{
// Fetcher gets something for us from somewhere...
return self::SUCCESS;
}
}
可以使用以下方法进行模拟:
\Mockery::getConfiguration()->setConstantsMap([
'Fetcher' => [
'SUCCESS' => 'success',
'FAILURE' => 'fail',
]
]);
$mock = \Mockery::mock('Fetcher');
var_dump($mock::SUCCESS); // (string) 'success'
var_dump($mock::FAILURE); // (string) 'fail'