如何在Python单元测试中Mock httpx的post()和get()调用?
问题解决:Mock httpx.get/post时的ModuleNotFoundError及正确实现
错误原因分析
- 模块识别问题:未将
src目录标记为Python包(缺少__init__.py),导致Python无法识别app模块,触发ModuleNotFoundError。 - Patch路径错误:要mock底层httpx调用,需针对Client类所在模块中导入的httpx实例进行patch,而非直接patch全局httpx或类方法本身。
- 测试逻辑偏差:原测试中导入方式、断言逻辑与Client方法的返回值不匹配,导致测试逻辑无效。
分步解决
1. 将src标记为Python包
在src目录下创建空的__init__.py文件,让Python识别其为可导入的包,目录结构如下:
project/ ├── src/ │ ├── __init__.py │ └── app.py └── test/ └── test.py
2. 修正测试文件(test/test.py)
调整导入逻辑、patch路径及断言逻辑,正确实现httpx方法的Mock:
from unittest.mock import patch, MagicMock import pytest from src.app import Client @pytest.mark.anyio @patch("src.app.httpx.get") def test_get(mock_get): # 模拟Response对象的行为 mock_response = MagicMock() mock_response.json.return_value = {"status": "passed"} mock_response.raise_for_status.return_value = None mock_get.return_value = mock_response cl = Client("test-url") result = cl.get_call("test") # 断言返回的JSON结果 assert result == {"status": "passed"} # 验证httpx.get的调用参数是否正确 mock_get.assert_called_once_with(url="test-url", params={"id": "test"}) @pytest.mark.anyio @patch("src.app.httpx.post") def test_post(mock_post): mock_response = MagicMock() mock_response.json.return_value = {"status": "passed"} mock_response.raise_for_status.return_value = None mock_post.return_value = mock_response cl = Client("test-url") result = cl.post_call({"data": "test"}) assert result == {"status": "passed"} mock_post.assert_called_once_with(url="test-url", json={"data": "test"})
3. 关键说明
- Patch路径规则:
patch需要mock被测试代码实际引用的对象。在src/app.py中,httpx是被导入到app模块内的,因此必须patchsrc.app.httpx.get(或对应相对路径),而非全局的httpx.get。 - 模拟Response对象:由于Client方法会调用
response.raise_for_status()和response.json(),需用MagicMock模拟这两个方法的行为,确保测试流程完整。 - 断言逻辑修正:Client的
get_call和post_call返回的是response.json()的字典结果,而非Response对象,因此直接断言字典即可。
验证运行
在项目根目录执行测试命令:
pytest test/test.py -v
内容的提问来源于stack exchange,提问作者Amogh Mishra
相关产品推荐
相关产品推荐

