downshiftorg/ezekiel

替代语法包装器,并添加了在PHPUnit中使用phpspec/prophecy的便利方法

2.0.0 2018-01-16 17:13 UTC

This package is not auto-updated.

Last update: 2024-09-15 04:47:22 UTC


README

Ezekiel为您提供了一个替代包装器,一些语法糖,以及一些便利方法,用于在PHPUnit中与phpspec/prophecy库一起工作。

对于使用Prophecy制作简单的存根和模拟,Ezekiel减少了大量的样板代码,并为您提供了一个简洁但相当表达性的语法,用于创建测试双对象。

一个简单的例子

假设你想快速制作一个测试替身。直接使用Prophecy,你可能需要这样做

<?php

$prophecy = $this->prophesize('SomeClass');
$prophecy->someMethod()->willReturn(false);

$stub = $prophecy->reveal();

使用Ezekiel,你只需编写如下代码

<?php

$stub = $this->stub('SomeClass', ['someMethod' => false]);

简短语法

对于非常简单的存根对象,当您不关心参数匹配或在不同的调用中返回不同的值时,您可以使用Ezekiel的简短语法

<?php

// stub a single method
$foo = $this->stub('Foo', ['someMethod' => false]);

// stub multiple methods
$bar = $this->stub('Bar', [
	'someMethod'  => false,
	'otherMethod' => true,
	'jimJam'      => 'some text'
]);

特殊返回值

存根/模拟可以通过使用特殊字符串 ~firstArg 返回传递的第一个参数

<?php

$stub = $this->stub('Foo', ['bar' => '~firstArg']);
$stub->bar('baz'); // returns 'baz'

存根/模拟可以通过使用特殊字符串 ~arg=X 返回传递的任意参数

<?php

$stub = $this->stub('Foo', ['bar' => '~arg=3']);
$stub->bar('baz', 'foo', 'herp'); // returns 'herp'

存根/模拟可以通过使用特殊字符串 ~self 返回自身

<?php

$stub = $this->stub('Foo', ['bar' => '~self']);
$stub->bar(); // returns $stub

存根/模拟也可以使用类似CoffeeScript的 @ 语法 返回测试用例公共属性的引用。如果您想在测试用例的 setUp() 方法中设置一个通用的存根或模拟,并修改不同测试方法中测试替身返回的不同部分,这非常有用。

<?php

$stub = $this->stub('Foo', ['bar' => '@myProp']);

$this->myProp = true;
$stub->bar(); // returns true

$this->myProp = false;
$stub->bar(); // returns false

存根/模拟也可以使用瘦箭头 -> 通过可调用函数名称传递参数

<?php

$stub = $this->stub('Foo', ['bar' => '~firstArg -> strtoupper']);
$stub->bar('baz'); // returns 'BAZ'

$stub = $this->stub('Foo', ['bar' => '~arg=2 -> strtoupper']);
$stub->bar('foo', 'bar'); // returns 'BAR'

存根/模拟也可以使用特殊字符串 ~joinArgs~joinArgs|DELIMETER 返回所有传递参数的拼接版本。这在非常简单的情况下非常有用,当使用较长的语法或 $this->calls() 不值得时,用于验证方法调用。

<?php

$stub = $this->stub('Foo', ['bar' => '~joinArgs|*']);
$stub->bar('baz', 'herp', 'derp'); // returns 'baz*herp*derp'

存根/模拟也可以返回匿名函数的调用结果,该匿名函数绑定到测试用例的 $this 并接收调用参数作为参数。

<?php

$stub = $this->stub('Foo', ['bar' => function($str) {
	return $this->someProp . ' ' . strtoupper($str) . '!';
}]);

$this->someProp = 'Hello';
$stub->bar('world'); // returns 'Hello WORLD!'

$this->someProp = 'Howdy';
$stub->bar('friends'); // returns 'Howdy FRIENDS!'

您还可以将 @prop 语法与匿名函数结合起来,以动态返回调用公共属性(该属性是匿名函数)的结果。

<?php

$stub = $this->stub('Foo', ['bar' => '@someProp']);

$this->someProp = function($arg1) {
	return $arg1 === 'foo' ? 'bar' : 'baz';
};

$stub->bar('foo'); // returns 'bar'
$stub->bar('qux'); // returns 'baz'

长语法

Ezekiel还可以接受一个长语法,允许您使用 'with''return' 关键字匹配参数并返回不同的值。

<?php

$stub = $this->stub('Foo', ['bar' => [
	['with' => ['jim'],  'return' => 'jam'],
	['with' => ['herp'], 'return' => 'derp'],
]]);

$stub->bar('jim');  // returns 'jam'
$stub->bar('herp'); // returns 'derp'

通常,'with' 属性必须是一个数组,并与正在匹配的参数对应。如果您不关心匹配某个参数,请传入字符串 * 作为通配符

<?php

$stub = $this->stub('Foo', ['bar' => [
	['with' => ['herp', 'derp'], 'return' => 'slurp'],
	['with' => ['herp', '*'],    'return' => false],
]]);

$stub->bar('herp', 'derp');  // returns 'slurp'
$stub->bar('herp', 'foo');   // returns false

您还可以传入非数组字符串值 '*' 以匹配任何组合的参数。这对于指定默认返回值非常有用。

<?php

$stub = $this->stub('Foo', ['bar' => [
	['with' => ['herp'], 'return' => 'derp'],
	['with' => '*',      'return' => 'my default value'],
]]);

$stub->bar('herp');  // returns 'derp'
$stub->bar('foo');   // returns 'my default value'
$stub->bar('bar');   // returns 'my default value'

创建模拟

默认情况下,Ezekiel创建测试存根,而不是模拟。这意味着我们正在指定系统被测试中测试替身的行为,但不是直接测试这些对象的使用方式。如果我们想创建包含期望的真实模拟对象,Ezekiel也可以帮助做到这一点。要创建包含期望的模拟对象,只需使用上面解释的较长语法,但用 'expect' 代替 'with'

<?php

$stub = $this->stub('Foo', ['bar' => [
	['expect' => ['jim'],  'return' => 'jam'],
]]);

// returns 'jam' and adds to assertion count
// if not invoked with expected args, produces PHPUnit failure
$stub->bar('jim');

您还可以使用 'times' 关键字指定模拟调用的预期次数。如果您没有设置 'times' 属性,Ezekiel 会认为您至少期望一次调用该方法及其参数。

<?php

$stub = $this->stub('Foo', ['bar' => [
	['expect' => ['jim'],  'return' => 'jam', 'times' => 1],
]]);

// causes PHPUnit failure because invoked too many times
$stub->bar('jim');
$stub->bar('jim');

Ezekiel 还允许您使用特殊字符串 '*' 进行简单的参数通配。

<?php

$stub = $this->stub('Foo', ['bar' => [
	['expect' => ['jim', '*'],  'return' => 'jam', 'times' => 1],
]]);

// causes PHPUnit pass because we wild-carded the second arg
$stub->bar('jim', 'jam');

您可以对单个方法做出多个预期。

<?php

$stub = $this->stub('Foo', ['bar' => [
	['expect' => ['foobar'],   'return' => 'hashbaz'],
	['expect' => ['jim', '*'], 'return' => 'jam', 'times' => 1],
	['expect' => ['herp', 1],  'return' => [1, 2, 3]],
]]);

如果您希望某个方法从不被调用,可以使用简写语法和特殊字符串 ~neverCalled 来表示。

<?php

$stub = $this->stub('Foo', ['bar' => '~neverCalled']);

// causes PHPUnit failure
$stub->bar();

检查调用

预言记录了您的测试替身方法调用,因此您可以执行一些酷炫的操作。

<?php

$prophecy->someMethod()->shouldHaveBeenCalled();

Ezekiel 提供了一种检查记录调用方法的替代方法,这有助于在没有编写疯狂的预测匹配器和回调的情况下,制作非常具体的测试间谍断言。

<?php

$stub = $this->stub('SomeClass', ['someMethod' => false]);
$stub->someMethod('foo', 'bar');

// returns array of all recorded invocations => [['foo', 'bar']];
$this->calls($stub, 'someMethod');

// returns arguments for first method invocation => ['foo', 'bar'];
$this->calls($stub, 'someMethod', $invocationIndex = 0);

// returns first argument for first method invocation => 'foo';
$this->calls($stub, 'someMethod', $invocationIndex = 0, $argumentIndex = 0);

您还可以将特殊字符串 ~last 作为第三个参数 $invocationIndex 的快捷方式,以返回请求方法的最后一次调用。如果您正在重用缓存存根并希望检查方法的最最近一次调用,这很有用。

<?php

$stub = $this->stub('SomeClass', ['someMethod' => false]);
$stub->someMethod(1);
$stub->someMethod(false);
$stub->someMethod('foo');

$this->calls($stub, 'someMethod', '~last');    // returns ['foo'];
$this->calls($stub, 'someMethod', '~last', 0); // returns 'foo';

注意:要使用 Ezekiel::calls(),您需要在您的清理方法中重置记录的调用,如下所示:$this->recordedCalls = []; Ezekiel 特性提供了一个 tearDown 方法来自动完成此操作,但如果您在自己的测试类中定义了自己的 tearDown 方法,您需要自己完成。

##转换类名

如果您想为命名空间类名创建快捷方式或以其他方式转换 Ezekiel 使用的类名,只需覆盖 Ezekiel 特性的 transformClass 方法。

<?php

protected transformClass($class) {
	return 'Acme\Foo\Bar\' . $class;
}

缓存

Ezekiel 还会缓存存根对象,如果您请求具有相同规格的存根,则会返回缓存值。这可以提供可感知的速度提升,因为预言不必多次动态重新创建存根类。它还允许您在设置不同测试方法中的相同存根对象时,不必担心性能问题。

<?php

$stub1 = $this->stub('SomeClass', ['foo' => 'blah blah']);
$stub2 = $this->stub('SomeClass', ['foo' => 'blah blah']);

$this->assertSame($stub1, $stub2); // true

安装

通过 Composer 拉取包。

{
    "require": {
        "downshiftorg/ezekiel": "0.2.*"
    }
}

然后,只需在一个扩展 PHPUnit_Framework_TestCase 的 TestCase 类中使用 Ezekiel 特性。

<?php

namespace Acme\Foo;

class FooTest extends \PHPUnit_Framework_Testcase {

	use \DownShift\Ezekiel\Ezekiel;

}

如果您希望 Ezekiel 自动在所有测试用例中可用,请将特性添加到一个所有测试用例类都扩展的 PHPUnit_Framework_TestCase 的子类中,如下所示

<?php

namespace Acme;

class AcmeTestCase extends \PHPUnit_Framework_Testcase {

	use \DownShift\Ezekiel\Ezekiel;

}