kartavik / php-mock
PHP-Mock 可以模拟内置的 PHP 函数(例如 time())。PHP-Mock 依赖于 PHP 的命名空间回退策略。无需进一步扩展。
Requires
- php: ^7.2
- phpunit/php-text-template: ^1
- wearesho-team/base-collection: ^1.0
Requires (Dev)
- phpunit/phpunit: ^8.0
- squizlabs/php_codesniffer: ^3.4
Suggests
- php-mock/php-mock-phpunit: Allows integration into PHPUnit testcase with the trait PHPMock.
README
PHP-Mock 是一个测试库,可以模拟非确定的内置 PHP 函数,如 time()
或 rand()
。这是通过 PHP 的命名空间回退策略 实现的
如果存在一个命名空间函数 [...],PHP 将回退到全局函数 [...]。
PHP-Mock 通过提供命名空间函数来利用此功能。即您必须在 非全局命名空间 上下文中以 未指定名称 调用该函数
namespace foo; $time = time(); // This call can be mocked, a call to \time() can't.
需求和限制
-
只有命名空间上下文中的 未指定名称 函数调用可以被模拟。例如,在命名空间
foo
中对time()
的调用是可模拟的,而对\time()
的调用则不是。 -
必须在测试类中对未指定名称的函数第一次调用之前定义模拟。这在 Bug #68541 中有记录。在大多数情况下,您可以忽略此限制,但如果您遇到此问题,可以在第一次调用之前调用
Mock::define()
。这将定义一个无副作用的命名空间函数,稍后可以启用。另一种有效的方法是在隔离进程中运行您的测试。
替代方案
如果您无法依赖或只是不想使用命名空间回退策略,有其他技术可以模拟内置 PHP 函数
-
PHPBuiltinMock 依赖于 APD 扩展。
-
MockFunction 是一个 PHPUnit 扩展。它使用 runkit 扩展。
-
UOPZ 是一个允许(例如)重命名和删除函数的 Zend 扩展。
-
vfsStream 是一个虚拟文件系统的流包装器。这可以帮助您编写覆盖 PHP 流函数(例如
fread()
或readdir()
)的测试。
安装
使用 Composer
composer require --dev kartavik/php-mock
用法
您不需要学习另一个 API。PHP-Mock 与这些测试框架集成了
- kartavik/php-mock-phpunit - PHPUnit 集成
注意:如果您计划使用上述提到的任何测试框架,您可以跳过阅读任何其他内容,直接转到特定的集成项目。
PHP-Mock API
创建一个 Mock
对象。您可以使用 MockBuilder
的流畅 API 来完成此操作
-
MockBuilder::setNamespace()
设置被模拟函数的目标命名空间。 -
MockBuilder::setName()
设置被模拟函数的名称(例如time()
)。 -
MockBuilder::setFunction()
设置具体的模拟实现。 -
MockBuilder::setFunctionProvider()
相对于MockBuilder::setFunction()
,设置模拟实现为FunctionProvider
FixedValueFunction
是一个简单的实现,它始终返回相同的值。
*
FixedMicrotime
是一个简单的实现,它始终返回相同的微时间戳。此类与FixedValueFunction
不同,因为它包含microtime()
的浮点数和字符串格式的转换器。-
FixedDate
是一个简单的实现,它始终返回固定时间戳的格式化日期。 -
SleepFunction
是一个sleep()
实现,它不会停止程序,而是增加一个Increment
,例如一个time()
模拟。 -
UsleepFunction
是一个usleep()
实现,它不会停止程序,而是增加一个Increment
,例如一个microtime()
模拟。
-
MockBuilder::build()
构建Mock
对象。
构建完 Mock
对象后,您必须调用 enable()
来在给定的命名空间中启用模拟。完成模拟后,您应通过在模拟实例上调用 disable()
来禁用它。
此示例说明了在命名空间 foo
中对无限定函数 time()
进行模拟。
namespace foo; use phpmock\MockBuilder; $builder = new MockBuilder(); $builder->setNamespace(__NAMESPACE__) ->setName("time") ->setFunction( function () { return 1417011228; } ); $mock = $builder->build(); // The mock is not enabled yet. assert (time() != 1417011228); $mock->enable(); assert (time() == 1417011228); // The mock is disabled and PHP's built-in time() is called. $mock->disable(); assert (time() != 1417011228);
您可以使用现有的 FixedValue
而不是使用 MockBuilder::setFunction()
来设置模拟函数。
namespace foo; use Kartavik\PHPMock\MockBuilder; use Kartavik\PHPMock\Functions\FixedValue; $builder = new MockBuilder(); $builder->setNamespace(__NAMESPACE__) ->setName("time") ->setFunctionProvider(new FixedValueFunction(1417011228)); $mock = $builder->build();
重置全局状态
启用的模拟会改变全局状态。如果后续测试运行了会无意中调用模拟的代码,这会破坏测试。因此,您应在测试用例后始终禁用模拟。您将必须禁用创建的模拟。您可以通过调用静态方法 Mock::disableAll()
为所有模拟执行此操作。
模拟环境
可以将几个模拟函数的复杂模拟环境分组在 Environment\Mock
中。
-
Environment\Mock::enable()
启用此环境中所有模拟的函数。 -
Environment\Mock::disable()
禁用此环境中所有模拟的函数。 -
Environment\Mock::define()
定义此环境中所有模拟的函数。
SleepEnvironmentBuilder
Environment\SleepBuilder
构建一个模拟环境,其中 sleep()
和 usleep()
立即返回。此外,它们会增加模拟的 date()
、time()
和 microtime()
中的时间量。
namespace foo; use Kartavik\Environment\SleepBuilder; $builder = new SleepEnvironmentBuilder(); $builder->addNamespace(__NAMESPACE__) ->setTimestamp(1417011228); $environment = $builder->build(); $environment->enable(); // This won't delay the test for 10 seconds, but increase time(). sleep(10); assert(1417011228 + 10 == time());
如果模拟函数应在不同的命名空间中,您可以使用 Environment\SleepBuilder::addNamespace()
添加更多命名空间。
作者和贡献者
Fork 贡献者 - Roman Vakura
作者 - 此项目是 php-mock/php-mock 的分支。