box / shmock
PHPUnit 模拟的简写
v2.0.1-beta2
2017-02-28 21:24 UTC
Requires
- php: >=5.4.0
- nicmart/string-template: 0.1.0
- phpunit/phpunit: >=3.7
- sebastian/diff: >=1.1
Requires (Dev)
- ext-augmented_types: *
- phpdocumentor/phpdocumentor: 2.*
This package is not auto-updated.
Last update: 2024-09-09 11:42:45 UTC
README
这是什么?
Shmock 是一个创建PHPUnit模拟的流畅替代方案,它使用了EasyMock的mock/replay概念,但使用闭包来定义模拟的范围。
<?php namespace Foo; /** * Here's a class we're trying to test yay. */ class Foo { private $foo = 0; private $incrementing_service = null; public function __construct(Incrementing_Service $incrementing_service) { $this->incrementing_service = $incrementing_service; } public function next_foo() { $this->foo = $this->incrementing_service->increment($this->foo); return $this->foo; } } /** * Our test case runs the same test case twice - once with the original PHPUnit mocking * syntax and a second time with Shmock syntax. */ class Foo_Test extends PHPUnit_Framework_TestCase { use \Shmock\Shmockers; // This enables the use of the Shmock helper methods (replicated below) public function test_phpunit_original_mocking_syntax() { // this is the original PHPUnit mock syntax $incrementing_service_mock = $this->getMock('\Foo\Incrementing_Service', array('increment')); $incrementing_service_mock->expects($this->once()) ->method('increment') ->with($this->equalTo(0)) ->will($this->returnValue(1)); $foo = new Foo($incrementing_service_mock); $this->assertEquals(1, $foo->next_foo(0)); } /** * Create a shmock representation for $class_name and configure expected * mock interaction with $conf_closure * @return Shmock A fully configured mock object * @note You do not need this protected method if you use the Shmockers trait, shown above */ protected function shmock($class_name, $conf_closure) { return \Shmock\Shmock::create_class($this, $class_name, $conf_closure); } public function test_shmock_syntax() { // here's shmock. Neat huh? $incrementing_service_mock = $this->shmock('\Foo\Incrementing_Service', function($shmock) { $shmock->increment(0)->return_value(1); }); $foo = new Foo($incrementing_service_mock); $this->assertEquals(1, $foo->next_foo(0)); } }
安装
Shmock 可以直接从 Packagist 安装。
"require": { "box/shmock": "1.0.0.x-dev" }
或者,你可以将 Shmock.php 和 Shmockers.php 下载到你的测试目录中,然后运行
require_once 'Shmock.php';
PHPUnit 应该已经包含在加载路径中,这样才能正常工作。
类型安全
Shmock 默认是类型安全的,并且会尝试告诉你何时使用了错误的模拟方法。当以下情况发生时,Shmock 会抛出错误:
- 将静态方法模拟为实例方法或反之。
- 将私有方法模拟为受保护或公共。
- 模拟一个不存在的方法 并且 未提供 __call / __callStatic 魔术方法。
可以通过在 shmock 闭包中调用 $mock_object->disable_strict_method_checking()
来禁用这些检查。我们还计划支持参数和返回值检查,如果它符合尚未定义的 PHPDoc 规范。
文档
http://box.github.io/shmock/namespaces/Shmock.html
Shmock 全部功能列表
<?php // This code could conceptually be part of a test method from the above Foo_Test class $inc_service = $this->shmock('\Foo\Incrementing_Service', function($my_class_shmock) // [1] { $my_class_shmock->no_args_method(); // [2] $my_class_shmock->two_arg_method('param1', 'param2'); // [3] $my_class_shmock->method_that_returns_a_number()->return_value(100); // [4] $my_class_shmock->method_that_gets_run_twice()->times(2); // [5] $my_class_shmock->method_that_gets_run_any_times()->any(); // [6] $my_class_shmock->method_puts_it_all_together('with', 'args')->times(2)->return_value(false); $my_class_shmock->method_returns_another_mock()->return_shmock('\Another_Namespace\Another_Class', function($another_class) // [7] { $another_class->order_matters(); // [8] $another_class->disable_original_constructor(); // [9a] $another_class->set_constructor_arguments(1, 'Foo'); // [9b] $another_class->method_dies_horribly()->throw_exception(new InvalidArgumentException()); // [10] $another_class->method_gets_stubbed(1,2)->will(function(PHPUnit_Framework_MockObject_Invocation $invocation) { $a = $invocation->parameters[0]; $b = $invocation->parameters[1]; return $a + $b; // [11] }); }); $my_class_shmock->shmock_class(function($Inc_Service) { $Inc_Service->my_static_method()->any()->return_value('This was returned inside the mock instance using the static:: prefix'); // [12] }); }) $another_class = $this->shmock_class('\Another_Namespace\Another_Class', function($Another_Class) // [13] { $Another_Class->a_static_method()->return_value(1); });
- Shmock 允许你在闭包内配置一个模拟对象。你使用的是一个感觉像真实对象一样的代理对象。
- 调用一个方法会设置一个期望,即它将被调用一次。
- 带参数调用一个方法会导致它期望在实际调用时使用这些参数。
- 你可以从特定的调用返回值。在示例中,当你调用该方法时,将返回值 100。
- 你可以指定方法将被调用的次数期望。默认情况下,期望一次。
- 或者你可以使用 any() 指定 "0 或更多" 次。
- 你可以嵌套 Shmock 调用,这让你可以优雅地定义你的模拟依赖关系。(如果你有两个方向的依赖关系,你总是可以直接
return_value($other_shmock)
并在其他地方定义它) - 在对象级别上,你可以指定 "顺序很重要",这意味着函数调用的顺序也应被断言。在底层,这会自动使用 PHPUnit 的
at(N)
调用。 - 在定义构造函数参数方面,你有一些选项。a) 你可以选择禁用原始构造函数。通常 PHPUnit 会运行原始构造函数。b) 你可以使用给定的参数运行原始构造函数。
- 当方法被调用时,你可以抛出异常而不是返回值。
- 更加复杂的是,你可以在函数被调用时执行一个任意的闭包。
- 如果你想模拟静态函数,你可以调用
shmock_class
,这将为你提供与实例相同的 Shmock 语义(在合理的情况下)。这在你想部分模拟一个对象,保留一些原始行为,但模拟出静态/受保护的函数时特别有用,这些函数可能存在于你正在测试的方法所依赖的上下文中。 - 你还可以独立于模拟实例来模拟一个类。
版权和许可
版权 2014 Box, Inc. 保留所有权利。
本软件基于Apache许可证2.0版本(以下简称“许可证”)许可;除非遵守许可证规定,否则不得使用此文件。您可以在以下地址获取许可证副本:
https://apache.ac.cn/licenses/LICENSE-2.0
除非适用法律要求或书面同意,否则在许可证下分发的软件是以“现状”为基础分发的,不提供任何形式的明示或暗示保证。有关许可证的具体语言、权限和限制,请参阅许可证。