为什么 Memcache::get() 的反射与文档不同?
Why is the Reflection for Memcache::get() different from the doc?
我正在尝试向 class 添加单元测试,该 class 使用 Memcache
作为在本地 memcached 守护程序上获取和存储密钥的服务。
我的问题是,尽管代码 运行 没问题,但当我尝试模拟 Memcache class 并调用 get() 方法时,我得到了这种错误:
ArgumentCountError: Too few arguments to function Mock_Memcache_b25e34cb::get(), 1 passed in /path/to/file.php on line 64 and exactly 3 expected
我怀疑是反射错误,所以我尝试了一个小脚本来测试它:
<?php
$class = new ReflectionClass(Memcache::class);
$getMethod = $class->getMethod('get');
$getParams = $getMethod->getParameters();
foreach($getParams as $param) {
var_dump((string) $param);
}
我得到的输出是
string(35) "Parameter #0 [ $param0 ]"
string(36) "Parameter #1 [ &$param1 ]"
string(36) "Parameter #2 [ &$param2 ]"
但是,在 the official PHP doc 找到的文档指出签名应该是:
Memcache::get(string $key, int &$flags = ?): string
或
Memcache::get(array $keys, array &$flags = ?): array
如何解释差异?为什么我的代码 运行 在生产环境中运行良好,但在 UT 环境中却失败了?除了为 Memcache 创建我自己的 mock class 之外,有没有办法解决这个问题?谢谢!
仅供参考,我目前获取模拟的方法是这样的:
protected function createMock($originalClassName)
{
return $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->disallowMockingUnknownTypes()
->getMock();
}
public function existsReturnsTrueOrFalse($value, $expected)
{
$memcacheMock = $this->createMock(\Memcache::class);
...
}
How can the discrepancy be explained?
大多数情况下,这只是扩展的文档不是最新的。
另一种解释可能是库反射错误。
看到反射的输出,我想说文档比反射更新。
Why does my code run fine on production but fails on the UT?
您的问题的单元测试的生产和测试之间的区别 运行 是模拟的创建:在生产中您不创建模拟,因此它不会失败。
Is there a way for me to solve this problem other than creating my own mock class for Memcache?
实际上创建您自己的模拟 class 听起来不是个坏主意。但是,您可能会考虑更进一步:
现在生产代码有 Memcache class 作为依赖项。你的测试已经告诉你,它的接口不稳定,依赖项拒绝测试(使用你的标准工具,如模拟)。
出于诸如此类的原因,通常建议包装第三方库。这可能是一个边缘案例,因为这与 PHP 扩展相关,该扩展可能不会那么不稳定,但也许包装为您提供了更多的开发、测试和维护空间。
您可以将您使用的 Memcache class 的 public 接口部分提取到接口本身中,例如内存缓存接口。然后,您可以创建一个实现接口的轻量级包装 -class,并在后台将方法调用委托给 Memcache 实例。
然后,您将生产代码中的 Memcache 对象替换为您的包装对象,并将 Memcache 的类型提示替换为您的 MemcacheInterface接口。
在测试中,您从界面创建模拟。
这将
- 记录每个使用的方法和参数(您在界面上执行此操作)。
- 允许在从(明确的)界面创建模拟时在 Phpunit 中创建模拟。
- 通过新包装器在中心位置访问第三方库的接口 class - 即使在生产中。
- 允许您将来在您的应用程序中切换到不同的 memcache 第三方库(但您不能,我为简洁起见提到它)。
我正在尝试向 class 添加单元测试,该 class 使用 Memcache
作为在本地 memcached 守护程序上获取和存储密钥的服务。
我的问题是,尽管代码 运行 没问题,但当我尝试模拟 Memcache class 并调用 get() 方法时,我得到了这种错误:
ArgumentCountError: Too few arguments to function Mock_Memcache_b25e34cb::get(), 1 passed in /path/to/file.php on line 64 and exactly 3 expected
我怀疑是反射错误,所以我尝试了一个小脚本来测试它:
<?php
$class = new ReflectionClass(Memcache::class);
$getMethod = $class->getMethod('get');
$getParams = $getMethod->getParameters();
foreach($getParams as $param) {
var_dump((string) $param);
}
我得到的输出是
string(35) "Parameter #0 [ $param0 ]"
string(36) "Parameter #1 [ &$param1 ]"
string(36) "Parameter #2 [ &$param2 ]"
但是,在 the official PHP doc 找到的文档指出签名应该是:
Memcache::get(string $key, int &$flags = ?): string
或
Memcache::get(array $keys, array &$flags = ?): array
如何解释差异?为什么我的代码 运行 在生产环境中运行良好,但在 UT 环境中却失败了?除了为 Memcache 创建我自己的 mock class 之外,有没有办法解决这个问题?谢谢!
仅供参考,我目前获取模拟的方法是这样的:
protected function createMock($originalClassName)
{
return $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->disallowMockingUnknownTypes()
->getMock();
}
public function existsReturnsTrueOrFalse($value, $expected)
{
$memcacheMock = $this->createMock(\Memcache::class);
...
}
How can the discrepancy be explained?
大多数情况下,这只是扩展的文档不是最新的。
另一种解释可能是库反射错误。
看到反射的输出,我想说文档比反射更新。
Why does my code run fine on production but fails on the UT?
您的问题的单元测试的生产和测试之间的区别 运行 是模拟的创建:在生产中您不创建模拟,因此它不会失败。
Is there a way for me to solve this problem other than creating my own mock class for Memcache?
实际上创建您自己的模拟 class 听起来不是个坏主意。但是,您可能会考虑更进一步:
现在生产代码有 Memcache class 作为依赖项。你的测试已经告诉你,它的接口不稳定,依赖项拒绝测试(使用你的标准工具,如模拟)。
出于诸如此类的原因,通常建议包装第三方库。这可能是一个边缘案例,因为这与 PHP 扩展相关,该扩展可能不会那么不稳定,但也许包装为您提供了更多的开发、测试和维护空间。
您可以将您使用的 Memcache class 的 public 接口部分提取到接口本身中,例如内存缓存接口。然后,您可以创建一个实现接口的轻量级包装 -class,并在后台将方法调用委托给 Memcache 实例。
然后,您将生产代码中的 Memcache 对象替换为您的包装对象,并将 Memcache 的类型提示替换为您的 MemcacheInterface接口。
在测试中,您从界面创建模拟。
这将
- 记录每个使用的方法和参数(您在界面上执行此操作)。
- 允许在从(明确的)界面创建模拟时在 Phpunit 中创建模拟。
- 通过新包装器在中心位置访问第三方库的接口 class - 即使在生产中。
- 允许您将来在您的应用程序中切换到不同的 memcache 第三方库(但您不能,我为简洁起见提到它)。