使用 unittest.mock 进行模拟
通过模拟依赖项隔离被测试的单元。
使用 unittest.mock 进行模拟 是 CoddyKit 上的免费 Python Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Python Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Python Academy 课程共包含 4 节课。
为什么使用 Mock?
Mock 会用可控的伪对象替代真实依赖项(数据库、API、文件系统),使测试运行快速、结果稳定且彼此隔离。
from unittest.mock import Mock
api = Mock()
api.get_user.return_value = {"id": 1, "name": "Alice"}
result = api.get_user(1)
print(result) # {'id': 1, 'name': 'Alice'}Mock 和 MagicMock
Mock 是基类。MagicMock 会自动配置魔术方法或双下划线方法,因此可以用作迭代器、上下文管理器等。
from unittest.mock import MagicMock
m = MagicMock()
m.__len__.return_value = 5
print(len(m)) # 5
m.__iter__.return_value = iter([1, 2, 3])
for x in m:
print(x)将 patch 用作装饰器
使用 @patch,即可在测试期间用 Mock 替换模块中的对象。
from unittest.mock import patch
def get_data():
import requests
return requests.get("https://api.example.com").json()
@patch("requests.get")
def test_get_data(mock_get):
mock_get.return_value.json.return_value = {"key": "value"}
result = get_data()
assert result == {"key": "value"}将 patch 用作上下文管理器
将 patch() 用作上下文管理器,可以实现更局部的 Mock。
from unittest.mock import patch
def test_open():
with patch("builtins.open", create=True) as mock_open:
mock_open.return_value.__enter__.return_value.read.return_value = "data"
with open("file.txt") as f:
assert f.read() == "data"patch.object
patch.object(target, attribute) 会修改现有对象或类上的特定属性。
from unittest.mock import patch
import mymodule
def test_method():
with patch.object(mymodule.MyClass, "fetch", return_value=42):
obj = mymodule.MyClass()
assert obj.fetch() == 42断言调用
Mock 会记录每次调用。使用 assert_called_once_with、assert_called_with 和 call_count 验证行为。
from unittest.mock import Mock
send = Mock()
send("hello", to="alice")
send.assert_called_once_with("hello", to="alice")
print(send.call_count) # 1return_value 和 side_effect
设置 return_value 来控制 Mock 的返回值。使用 side_effect 引发异常,或让连续调用返回不同的值。
from unittest.mock import Mock
m = Mock(side_effect=[1, 2, ValueError("done")])
print(m()) # 1
print(m()) # 2
m() # raises ValueErrorspec:类型安全的 Mock
传入 spec=MyClass,即可将 Mock 限制为只能使用真实类中存在的属性。
from unittest.mock import Mock
class Calc:
def add(self, a, b): return a + b
m = Mock(spec=Calc)
m.add(1, 2) # OK
# m.subtract(1, 2) # AttributeError: Mock does not have 'subtract'patch.dict
使用 patch.dict 临时修改字典(例如环境变量或模块级配置)。
from unittest.mock import patch
import os
def test_env():
with patch.dict(os.environ, {"DEBUG": "true"}):
assert os.environ["DEBUG"] == "true"
assert "DEBUG" not in os.environ在 pytest 中使用 pytest-mock 进行 Mock
pytest-mock 提供一个 mocker 测试夹具,用于封装 unittest.mock,并在每个测试后自动完成清理。
# pip install pytest-mock
def test_service(mocker):
mocker.patch("mymodule.requests.get", return_value=mocker.Mock(
json=lambda: {"status": "ok"}
))
from mymodule import call_api
assert call_api()["status"] == "ok"用于协程的 AsyncMock
使用 AsyncMock 模拟需要等待的异步函数。
from unittest.mock import AsyncMock, patch
import asyncio
async def fetch():
import httpx
async with httpx.AsyncClient() as c:
r = await c.get("https://example.com")
return r.json()
def test_fetch():
with patch("httpx.AsyncClient.get", new_callable=AsyncMock) as m:
m.return_value.json.return_value = {"ok": True}
result = asyncio.run(fetch())
assert result["ok"] is True快速检查
在 Mock 上设置哪个属性,可以控制调用它时返回的值?
回顾
unittest.mock 提供 Mock、MagicMock、patch 和 AsyncMock。使用 return_value 和 side_effect 控制行为,并使用断言方法验证 Mock 的调用方式。
常见问题解答
「使用 unittest.mock 进行模拟」课时是免费的吗?
是的 — 「使用 unittest.mock 进行模拟」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Python Academy 课程的其余内容,请升级到 CoddyKit PRO。 Python Academy 课程共包含 4 节课。
「使用 unittest.mock 进行模拟」这节课中我会学到什么?
通过模拟依赖项隔离被测试的单元。 你通过在浏览器中直接运行的动手代码来练习 Python Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Python Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Python Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「使用 unittest.mock 进行模拟」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Python Academy 课中编写并运行代码吗?
能。每节 Python Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 编写您的第一个 pytest 测试
- 固件与设置/拆卸
- 参数化与测试覆盖率
- 使用 unittest.mock 进行模拟