gstarczyk / mimic
基于Java Mockito的PHP模拟和存根工具
Requires
- php: ^7.2
Requires (Dev)
- phpunit/phpunit: ^8.0
This package is auto-updated.
Last update: 2024-09-14 15:06:08 UTC
README
用于创建类模拟的简单工具。
它在Java工具Mockito的启发下开发。
目录
安装
要安装,只需将 'gstarczyk/mimic' 添加到您的 composer.json 中
composer require --dev gstarczyk/mimic
要求
Mimic 至少需要 PHP 5.5 版本。
用法
创建模拟
$mock = Gstarczyk\Mimic\Mimic::mock(SomeClass::class);
或如果导入 Mimic 类,则只需 $mock = Mimic::mock(SomeClass::class);
就这样,您只需创建模拟
您可以使用 InitMock 辅助函数,它使创建模拟更方便。
定义具有 @var
和 @mock
注释的测试用例类属性,然后在每次测试之前运行 Mimic::initMocks($testCase)
。
对于任何定义并正确标记的属性,将生成模拟并将其分配给属性。
您还可以定义测试对象的属性并使用 @var
和 @injectMocks
注释进行标记。测试对象将使用构造函数中的模拟创建。测试对象的类构造函数必须仅要求对象。这些所需的对象应定义为模拟。
class MyService
{
}
class MyOtherService
{
}
class MyServiceToTest
{
/** @var MyService */
private $service;
/** @var MyOtherService */
private $otherService;
public function __construct(MyService $service, MyOtherService $otherService)
{
$this->service = $service;
$this->otherService = $otherService;
}
}
class TestCase extends \SomeFameousPhpTestFramework_TestCase
{
/**
* @var \MyNameSpace\MyServiceToTest
* @injectMocks
*/
private $testedObject;
/**
* @var \MyNameSpace\MyService
* @mock
*/
private $mock1;
/**
* @var \MyNameSpace\MyOtherService
* @mock
*/
private $mock2;
protected function setUp()
{
Mimic::initMocks($this);
}
}
遗憾的是,由于 PHP 注释的性质,您必须在 "var" 注释中使用 完整类名 或提供测试用例文件的路径(以扫描导入)。
class TestCase extends \SomeFameousPhpTestFramework_TestCase
{
/**
* @var MyServiceToTest
* @injectMocks
*/
private $testedObject;
/**
* @var MyService
* @mock
*/
private $mock1;
/**
* @var MyOtherService
* @mock
*/
private $mock2;
protected function setUp()
{
Mimic::initMocks($this, __FILE__);
}
}
定义存根
要定义存根(模拟对象的操作),请使用 Mimic::when() 方法。
$mock = Mimic::mock(SomeClass::class);
Mimic::when($mock)
->invoke('someMethod') // choose method
->withoutArguments() // choose arguments
->willReturn('some value'); // define behaviour
要选择参数,您可以使用三种方法
withoutArguments()
- 当不应传递任何参数时withAnyArguments()
- 当您不关心参数时with(....)
- 当您想指定为哪些参数定义行为时equal() value matcher will be used to check arguments. You can refine matching by using value matchers in arguments list (see examples).
要定义存根的行为,请使用以下列出的方法
willReturn(mixed $value)
- 指定方法返回的值willReturnCallbackResult(Closure $callback)
- 动态准备返回值。调用方法时传递的参数将传递给 $callback。willThrow(Exception $exception)
- 在调用期间抛出异常
您可以为每个参数集定义不同的操作
Mimic::when($mock)
->invoke('someMethod')
->with(10, 20)
->willReturn('some value');
Mimic::when($mock)
->invoke('someMethod')
->with(100, 200)
->willReturnCallbackResult(function($arg1, $arg2) {
return $arg1 + $arg2;
});
您可以使用值匹配器来细化参数列表
Mimic::when($mock)
->invoke('someMethod')
->with(Match::anyInteger(), 20)
->willReturn('some value');
如果您想存根连续调用,则可以使用专门的存根构建器
Mimic::when($mock)
->consecutiveInvoke('someMethod')
->willReturn('first')
->thenReturn('second')
->thenReturn('third');
使用此构建器时,您不能定义参数,只能定义连续的行为(默认使用 withAnyArguments()
)。
方法调用的验证
要验证您的模拟对象的某个方法已被调用,请使用 Mimic::verify() 方法。
Mimic::verify($mock)
->method('someMethod')
->with(100, 200)
->wasCalled(Times::exactly(1));
同样,您可以使用值匹配器来细化参数列表
Mimic::verify($mock)
->method('someMethod')
->with(Match::anyInteger(), 200)
->wasCalled(Times::exactly(1));
您还可以验证连续调用
Mimic::verify($mock)
->consecutiveMethodInvocations('getMore')
->wasCalledWith('a')
->thenWith('b')
->thenWithAnyArguments()
->thenWith('c')
->thenWithoutArguments();
或
Mimic::verify($mock)
->consecutiveMethodInvocations('getMore')
->wasCalledWithoutArguments()
...
或
Mimic::verify($mock)
->consecutiveMethodInvocations('getMore')
->wasCalledWithAnyArguments()
...
可用的值匹配器
Match::equal($value)
- 使用 "==" 运算符进行比较Match::same($value)
- 使用 "===" 运算符进行比较Match::anyString()
Match::anyInteger()
Match::anyFloat()
Match::anyTraversable()
- 匹配数组和实现了 \Traversable 的对象Match::anyObject($className = null)
Match::stringStartsWith($prefix)
Match::stringEndsWith($prefix)
可用的调用次数验证器
Times::exactly($value)
Times::atLeast($value)
Times::once()
- 与Times::exactly(1)
相同Times::never()
- 与Times::exactly(0)
相同
参数捕获
有时您需要对已验证方法的参数进行更复杂的断言。您可以使用参数捕获器,并在捕获的数据上使用您喜欢的断言工具。
$mock->someMethod('a');
$mock->someMethod('b');
$mock->someMethod('c', 'd');
$captor = new ArgumentsCaptor();
Mimic::verify($mock)
->method('someMethod')
->with($captor)
->wasCalled(Times::exactly(3));
$capturedData = $captor->getValues();
$expectedData = [
['a'],
['b'],
['c', 'd']
];
Assert::assertEquals($expectedData, $capturedData);
监视一个方法
有时您可能需要调用模拟的原始方法。
Mimic::spy($mock, 'someMethod');
$mock->someMethod('a', 'b');
// The method 'someMethod' of the original class will be call,
// The invocation is still be counted
示例
让我们假设我们有以下类
class Person
{
private $birthDate;
public function __construct(\DateTimeImmutable $birthDate)
{
$this->birthDate = $birthDate;
}
public function getAge()
{
$now = new \DateTimeImmutable('now');
$age = $now->diff($this->birthDate);
return $age->y;
}
}
class AgeVerifier
{
/**
* @param Person $customer
* @return bool
*/
public function isAdult(Person $customer)
{
return $customer->getAge() >= 18;
}
}
并且我们想测试 AgeVerifier::isAdult() 方法,但不需要在 Person::getAge() 中运行代码。
要做这样的“复杂”操作,我们需要
$person = Mimic::mock(Person::class);
$verifier = new AgeVerifier();
Mimic::when($person)
->invoke('getAge')
->withoutArguments()
->willReturn(20);
$result = $verifier->isAdult($person);
assert($result === true);
并且如果我们想验证 AgeVerifier 是否使用了 Person::getAge
$person = Mimic::mock(Person::class);
$verifier = new AgeVerifier();
$verifier->isAdult($person);
Mimic::verify($person)
->method('getAge')
->withoutArguments()
->wasCalled(Times::once());