downshiftorg / ezekiel
替代语法包装器,并添加了在PHPUnit中使用phpspec/prophecy的便利方法
Requires
- php: >=5.4.0
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; }