jcid / phpunit-mock-extension
PHPUnit Mock 扩展
0.1.0
2016-01-10 10:53 UTC
Requires
- php: ~5.6 | ~7.0
- phpunit/phpunit: ^5.1
This package is auto-updated.
Last update: 2024-08-23 19:34:26 UTC
README
一个 PHP 扩展,允许对方法进行多次调用,具有不同的计数和不同的参数。
安装
composer require jcid/phpunit-mock-extension
问题
目前,如果您想对 PHP Unit 模拟对象进行多次方法调用,具有不同的参数和返回语句,您可以使用 $this->at($index)
方法。这个方法的问题是不能检查方法是否被调用得太多次,比如下面的例子中的第三次调用。这是因为 PHPUnit 不使用参数来选择匹配器。
class ExampleTest extends PHPUnit_Framework_TestCase { public function testExample() { $mock = $this->getMock(\stdClass::class, ['mymethod']); $mock->expects($this->at(0)) ->method('mymethod') ->with('aaa') ->willReturn('bbb'); $mock->expects($this->at(1)) ->method('mymethod') ->with('bbb') ->willReturn('ccc'); $this->assertSame('bbb', $mock->mymethod('aaa')); $this->assertSame('ccc', $mock->mymethod('bbb')); $mock->mymethod('different'); } }
另一个解决方案是使用逻辑或方法来定义参数。但我个人不喜欢这个方法,因为它将参数与返回值解耦,并且您无法控制参数和返回值的顺序或组合。
class ExampleTest extends PHPUnit_Framework_TestCase { public function testLogicOr() { $mock = $this->getMock(\stdClass::class, ['mymethod']); $mock->expects($this->exactly(2)) ->method('mymethod') ->with($this->logicalOr( $this->equalTo('aaa'), $this->equalTo('bbb') )) ->will($this->onConsecutiveCalls('bbb', 'ccc')); $this->assertSame('bbb', $mock->mymethod('aaa')); $this->assertSame('ccc', $mock->mymethod('bbb')); } }
但还有第三个解决方案由 PHPUnit 提供,即 $this->returnValueMap(*array*)
方法。这个方法有点奇怪,因为数组的最后一个参数是返回值。但它将参数和返回值耦合起来,这是很好的。现在您也可以检查方法是否以正确的组合被调用多少次。
class ExampleTest extends PHPUnit_Framework_TestCase { public function testReturnValueMap() { $mock = $this->getMock(\stdClass::class, ['mymethod']); $mock->expects($this->exactly(2)) ->method('mymethod') ->will($this->returnValueMap([ ['aaa', 'bbb'], ['bbb', 'ccc'], ])); $this->assertSame('bbb', $mock->mymethod('aaa')); $this->assertSame('ccc', $mock->mymethod('bbb')); } }
使用 $this->returnValueMap(*array*)
方法的第二个问题是,参数使用严格的比较检查,如果您使用值持有者,比如我们这样,这将失败。
class ExampleTest extends PHPUnit_Framework_TestCase { public function testReturnValueMapStrict() { $mock = $this->getMock(\stdClass::class, ['mymethod']); $mock->expects($this->exactly(2)) ->method('mymethod') ->will($this->returnValueMap([ [new DummyValueHolder('aaa'), 'bbb'], ['bbb', 'ccc'], ])); $this->assertSame('bbb', $mock->mymethod(new DummyValueHolder('aaa'))); $this->assertSame('ccc', $mock->mymethod('bbb')); } } class DummyValueHolder { private $data; public function __construct($data) { $this->data = $data; } }
我们的解决方案
我们创建了这样一个库,它使用自定义 MockObject 匹配器和自定义 MockObject 来决定如何以及多少次返回特定的值。这个想法基于 $this->at($index)
的理念,但具有我们想要的全部功能。
我们使用自定义构建器来创建对特定方法调用序列,然后使用构建器创建的自定义匹配器和它提供的返回存根来定义 expects
。
use PHPUnit\Extensions\MockObject\Stub\ReturnMapping\Builder; class ExampleTest extends PHPUnit_Framework_TestCase { public function testSolution() { $builder = new Builder(); $builder->expects($this->exactly(2)) ->with(new DummyValueHolder('aaa')) ->willReturnSelf(); $builder->expects($this->once()) ->with('second method call matcher') ->willReturn('second return'); $builder->expects($this->exactly(2)) ->with('third method call matcher') ->willReturn('third return'); $dummy = $this->getMock(\stdClass::class, ['test']); $dummy->expects($builder->matcher())->method('test')->will($builder->mapper()); $this->assertSame($dummy, $dummy->test(new DummyValueHolder('aaa'))); $this->assertSame($dummy, $dummy->test(new DummyValueHolder('aaa'))); $this->assertSame('second return', $dummy->test('second method call matcher')); $this->assertSame('third return', $dummy->test('third method call matcher')); $this->assertSame('third return', $dummy->test('third method call matcher')); } } class DummyValueHolder { private $data; public function __construct($data) { $this->data = $data; } }
灵感
这个库受到了 etsy/phpunit-extensions
的启发。