You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让PHPUnit Mock对象根据参数返回不同文件内容?

问题:PHPUnit Mock Redis服务根据Key返回对应JSON文件内容失败

问题背景

  • 需要Mock Redis服务的getAuthData方法,根据传入的key返回本地JSON文件内容,用于测试复杂用户权限
  • 当前使用PHPUnit 9,即将升级到PHPUnit 10,需避免使用at/withConsecutive等废弃方法
  • 尝试用returnCallback实现,但触发类型错误:TypeError: Mock_AuthRedisServiceInterface_4131c2db::getAuthData(): Return value must be of type string, null returned

错误原因

回调函数无法访问外部作用域的$authJson和$userGlobalsJson变量,导致匹配不到key时返回null,违反方法的返回类型约束。

解决方案

通过PHP的use关键字让回调函数捕获外部变量,同时添加默认返回值避免返回null:

修改后的代码:

private function mockAuthRedisService(
    string $authFile = 'auth_admin',
    string $userGlobalsFile = 'user_globals_admin'
): MockObject|AuthRedisServiceInterface {
    $authJson = $this->readfile($authFile);
    $userGlobalsJson = $this->readfile($userGlobalsFile);

    $redisService = $this->getMockBuilder(AuthRedisServiceInterface::class)
                         ->disableOriginalConstructor()
                         ->getMock();

    $redisService->expects($this->any())
                 ->method('getAuthData')
                 ->will(
                     $this->returnCallback(
                         // 使用use关键字引入外部变量
                         function(string $keyName) use ($authJson, $userGlobalsJson) {
                             if (str_contains($keyName, 'authInfo')) { // PHP 8+推荐用str_contains替代strstr
                                 return $authJson;
                             }
                             if (str_contains($keyName, 'userGlobals')) {
                                 return $userGlobalsJson;
                             }
                             // 添加默认返回值,避免返回null违反类型约束
                             return json_encode([]);
                         }
                     )
                 );

    return $redisService;
}

private function readfile(string $name): string
{
    $path = __DIR__ . '/Unit/Security/test_data_files/' . $name . '.json';
    // 可选:添加文件存在校验,提前发现缺失的测试数据
    if (!file_exists($path)) {
        throw new RuntimeException("测试数据文件不存在:{$path}");
    }
    return file_get_contents($path);
}

关键修改点

  • 回调函数通过use ($authJson, $userGlobalsJson)捕获外部变量,解决作用域访问问题
  • 用str_contains替代strstr(PHP 8+语义更清晰,需兼容PHP 7.x可保留strstr)
  • 添加默认返回值(如空JSON字符串),确保所有输入都符合方法的返回类型要求
  • 可选:在readfile中增加文件存在校验,提前排查测试数据缺失问题

额外优化建议

如果需要支持更多key与文件的映射,可改用数组配置的方式提升可维护性:

private function mockAuthRedisService(array $keyFileMap = [
    'authInfo' => 'auth_admin',
    'userGlobals' => 'user_globals_admin'
]): MockObject|AuthRedisServiceInterface {
    // 预加载所有需要的JSON内容
    $jsonMap = [];
    foreach ($keyFileMap as $keyKeyword => $fileName) {
        $jsonMap[$keyKeyword] = $this->readfile($fileName);
    }

    $redisService = $this->getMockBuilder(AuthRedisServiceInterface::class)
                         ->disableOriginalConstructor()
                         ->getMock();

    $redisService->expects($this->any())
                 ->method('getAuthData')
                 ->will(
                     $this->returnCallback(
                         function(string $keyName) use ($jsonMap) {
                             foreach ($jsonMap as $keyword => $jsonContent) {
                                 if (str_contains($keyName, $keyword)) {
                                     return $jsonContent;
                                 }
                             }
                             return json_encode([]);
                         }
                     )
                 );

    return $redisService;
}

后续新增key映射时,只需修改$keyFileMap参数即可,无需调整回调逻辑。

内容的提问来源于stack exchange,提问作者Calamity Jane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 02:12:31