0Pricing
Python Academy · 课时

使用 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)  # 1

return_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 ValueError

spec:类型安全的 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 编写您的第一个 pytest 测试
  2. 固件与设置/拆卸
  3. 参数化与测试覆盖率
  4. 使用 unittest.mock 进行模拟
← 返回 Python Academy