net-tools / phpunit-given
PHPUnit 的 ReturnValueMap 和 ReturnCallback 的替代方案。从 Prophecy 迁移的便捷解决方案。
Requires
- php: >=8.0
- phpunit/phpunit: ^10.0||^11.0
Requires (Dev)
- phpstan/phpstan: ^1.9
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 许可证下发布。