olvlvl / phpunit-given
PHPUnit的ReturnValueMap和ReturnCallback的替代品。从Prophecy迁移的便捷解决方案。
Requires
- php: >=8.0
- phpunit/phpunit: ^9.0
Requires (Dev)
- phpstan/phpstan: ^1.9
This package is auto-updated.
Last update: 2024-09-05 18:10:49 UTC
README
olvlvl/phpunit-given提供对PHPUnit的ReturnValueMap和ReturnCallback的替代方案,以及从Prophecy迁移的便捷解决方案。
免责声明
在大多数情况下,可以使用带有match
的ReturnCallback
有效地。如果您对这些方案感到满意且不需要额外功能,则无需使用此包。
使用方法
以下是一个简单示例,更多用例见下方。
use olvlvl\Given\GivenTrait; use PHPUnit\Framework\TestCase; final class IntegerNameTest extends TestCase { use GivenTrait; // <-- adds the method 'given' public function testName(): void { $mock = $this->createMock(IntegerName::class); $mock->method('name')->will($this ->given(new Integer(6))->return("six") ->given(new Integer(12))->return("twelve") ->default()->throw(LogicException::class) ); $this->assertEquals("six", $mock->name(new Integer(6))); $this->assertEquals("twelve", $mock->name(new Integer(12))); $this->expectException(LogicException::class); $mock->name(new Integer(99)); } }
安装
composer require olvlvl/phpunit-given
动机
从Prophecy、C# Moq、Golang Mock或Kotlin Mockk迁移过来,人们会期望以下至少一个示例能够工作,但它们并不工作。
$mock = $this->createMock(IntegerName::class); $mock ->method('name') ->with(new Integer(6)) ->willReturn("six"); $mock ->method('name') ->with(new Integer(12)) ->willReturn("twelve"); // the next line crashes with: Expectation failed $this->assertEquals("six", $mock->name(new Integer(6)));
$mock = $this->createMock(IntegerName::class); $mock ->method('name') ->with(new Integer(6))->willReturn("six"); // the next line crashes with: Method parameters already configured ->with(new Integer(12))->willReturn("twelve"); $this->assertEquals("six", $mock->name(new Integer(6)));
为了根据特定参数返回值,人们会期望使用ReturnValueMap或ReturnCallback。看起来ReturnValueMap
很简单,但由于它寻找精确匹配,当参数中包含对象时(除非它们是相同的实例),它将失败。此外,ReturnValueMap
不支持约束,您无法使用它做任何事情复杂。这让我们只剩下了ReturnCallback
,它可以与match
有效地一起使用,但需要在测试中引入逻辑,这是一种被禁止的做法。
$mock = $this->createMock(IntegerName::class); $mock->method('name')->willReturnCallback(fn (Integer $int) => match ($int) { new Integer(6) => 'six', new Integer(12) => 'twelve', default => throw new Exception }));
创建olvlvl/phpunit-given的动机是有一个类似于其他测试框架中的替代方案,它类似于ReturnValueMap和ReturnCallback,并且允许从Prophecy轻松迁移。
一些PHPUnit问题,仅供参考
用例
比较对象
ReturnValueMap 无法与对象一起使用,因为它在比较参数时使用严格相等性。以下代码会抛出 TypeError
异常,因为 ReturnValueMap
找不到匹配项,并默认返回一个 null
值。
$mock = $this->createMock(IntegerName::class); $mock->method('name')->will($this->returnValueMap([ [ new Integer(6), "six" ], [ new Integer(12), "twelve" ], ])); $mock->name(new Integer(6)); // throws TypeError
olvlvl/phpunit-given 使用 Assert::equalTo()
替换值,并使用约束比较参数。参数中有对象并不成问题。
$mock = $this->createMock(IntegerName::class); $mock->method('name')->will($this ->given(new Integer(6))->return("six") ->given(new Integer(12))->return("twelve") ); $this->assertEquals("six", $mock->name(new Integer(6))); $this->assertEquals("twelve", $mock->name(new Integer(12)));
注意: 您可以使用 Assert::identicalTo()
来检查是否为同一实例。
使用约束
我们已经确定,内部使用 Assert::equalTo()
替换值。您也可以使用约束而不是值
$mock = $this->createMock(IntegerName::class); $mock->method('name')->will($this ->given(Assert::lessThan(new Integer(6)))->return('too small') ->given(Assert::greaterThan(new Integer(9)))->return('too big') ->default()->return('just right') // `default()` is a shortcut for `given(Assert::anything())` ); $this->assertEquals("too small", $mock->name(new Integer(5))); $this->assertEquals("too big", $mock->name(new Integer(10))); $this->assertEquals("just right", $mock->name(new Integer(6))); $this->assertEquals("just right", $mock->name(new Integer(9)));
当然,您也可以使用 ReturnCallback
,尽管它会向测试中添加逻辑。使用您感觉更舒适的方法。
$mock = $this->createMock(IntegerName::class); $mock->method('name')->willReturnCallback(fn (Integer $int) => match (true) { $int < new Integer(6) => 'too small', $int > new Integer(9) => 'too big', default => 'just right'; }));
从 Prophecy 迁移
olvlvl/phpunit-given 是从 Prophecy 迁移的一个方便的解决方案,因为代码非常相似
$container = $this->prophesize(ContainerInterface::class); $container->has('serviceA')->willReturn(true); $container->has('serviceB')->willReturn(false);
$container = $this->createMock(ContainerInterface::class); $container->method('has')->will($this ->given('serviceA')->return(true) ->given('serviceB')->return(false) );
throw()
是 willThrow()
的替代方案,您可以对 return()
和 throw()
进行不匹配
$container = $this->prophesize(ContainerInterface::class); $container->get('serviceA')->willReturn($serviceA); $container->get('serviceB')->willThrow(new LogicException());
$container = $this->createMock(ContainerInterface::class); $container->method('get')->will($this ->given('serviceA')->return($serviceA) ->given('serviceB')->throw(LogicException::class) );
与 Prophecy 相反,olvlvl/phpunit-given 默认不返回 null
,而是抛出一个异常
$mock = $this->createMock(IntegerName::class); $mock->method('name')->will($this ->given(new Integer(6))->return("six") ->given(new Integer(12))->return("twelve") ); $mock->name(new Integer(13)); // throws an exception
LogicException : Unexpected invocation: Test\olvlvl\Given\Acme\IntegerName::name(Test\olvlvl\Given\Acme\Integer Object (...)): string, didn't match any of the constraints: [ [ is equal to Test\olvlvl\Given\Acme\Integer Object &000000000000000c0000000000000000 (
'value' => 6
) ], [ is equal to Test\olvlvl\Given\Acme\Integer Object &00000000000001af0000000000000000 (
'value' => 12
) ] ]
持续集成
项目由 GitHub actions 持续测试。
行为准则
本项目遵循 贡献者行为准则。通过参与本项目及其社区,您应遵守此准则。
贡献
有关详细信息,请参阅 CONTRIBUTING。
许可证
olvlvl/phpunit-given 在 BSD-3-Clause 许可下发布。