Python如何正确Mock urllib3.PoolManager.request方法解决单元测试报错
问题解决:urllib3 PoolManager request方法mock失效
错误原因
- 不符合Python mock的路径规则:mock生效的前提是patch目标为业务代码实际引用的对象,你当前patch的是urllib3原生库的
PoolManager.request类方法,但你的业务代码在模块顶层就已经完成了http = urllib3.PoolManager()的实例化,测试运行导入业务模块后,这个全局http实例已经创建完成,后续对类方法的patch不会作用到已存在的实例上。 - 断言逻辑错误:
@patch('urllib3.PoolManager.request')注入的mock_instance本身就是request方法的mock对象,你调用mock_instance.request.assert_called_once()相当于尝试访问mock方法的request属性,自然匹配不到实际调用。
正确实现方案
方案1:patch业务模块中已实例化的http对象的request方法
假设你的业务代码存放在service.py模块中,直接patch该模块下的全局http实例的request方法即可:
# 注意将service替换为你业务代码实际所在的模块名 @patch('service.http.request') def test_lambda_handler(self, mock_request): # 配置mock返回值,注意urllib3返回的data默认是bytes类型 mock_request.return_value = Mock(status = 200, data = b"success") # 执行业务函数 res = func(event, []) # 直接断言mock的request方法被调用 mock_request.assert_called_once() # 也可以补充断言调用参数是否符合预期 # mock_request.assert_called_with('POST', 预期url, body=预期编码后的请求体)
方案2:调整业务代码的实例化逻辑,提升可测试性
如果不想依赖全局实例,可以将PoolManager的实例化放到函数内部,或者通过依赖注入传入,后续mock更灵活:
# 调整后的业务代码 def func(event, context, http_client=None): http = http_client or urllib3.PoolManager() # 原有业务逻辑不变 resp = http.request('POST', url, body=encoded_msg) # ...
对应的测试代码不需要用patch装饰器,直接传入mock对象即可:
def test_lambda_handler(self): mock_http = Mock() mock_http.request.return_value = Mock(status=200, data=b"success") res = func(event, [], http_client=mock_http) mock_http.request.assert_called_once()
内容的提问来源于stack exchange,提问作者ebalya
相关产品推荐
相关产品推荐

