netrivet/ezekiel

此包已废弃,不再维护。作者建议使用downshiftorg/ezekiel包。

替代语法包装器和在PHPUnit中使用phpspec/prophecy的便捷方法

2.0.0 2018-01-16 17:13 UTC

This package is not auto-updated.

Last update: 2022-02-01 12:45:19 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|DELIMITER。这在非常简单的情况下很有用,当你不值得使用较长的语法或$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方法,但如果您在测试类中定义了自己的,您需要自己完成。

##转换类名

如果您想缩短命名空间类名或以其他方式转换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;

}