Python中mock.patch用法 单元测试mock模块patch方法模拟对象解析

Python中mock.patch用法 单元测试mock模块patch方法模拟对象解析
最新回答
最美的风信子¢

2022-07-01 14:41:09

mock.patch 是 Python unittest.mock 模块中的核心工具,用于在单元测试中临时替换对象(如函数、类、方法等),通过装饰器或上下文管理器实现测试隔离,并在结束后自动恢复原状。

核心功能与常见用途
  1. 替换函数返回值

    模拟外部依赖(如网络请求、数据库查询)的行为,避免真实调用。

    示例:模拟 requests.get() 返回固定响应,测试 fetch_data() 函数:from unittest.mock import patchimport requestsdef fetch_data(): response = requests.get('

    https://example.com'
    ) return response.status_code@patch('requests.get')def test_fetch_data(mock_get): mock_get.return_value.status_code = 200 assert fetch_data() == 200

    关键点:patch 的路径需为被测试代码中实际导入的名称(如 requests.get 而非模块定义位置)。

  2. 控制作用范围

    装饰器:适用于整个测试函数需替换的场景。@patch('module.ClassName')def test_something(mock_class): pass

    上下文管理器(with 语句):仅在代码块内替换。def test_something(): with patch('module.ClassName') as mock_class: pass

  3. 模拟类方法与实例方法

    类方法:直接替换类上的方法(所有实例共享)。class MyClass: def method(self): return 'real'@patch.object(MyClass, 'method', return_value='mocked')def test_class_method(mock_method): obj = MyClass() assert obj.method() == 'mocked'

    实例方法:需先创建实例,再替换实例方法(仅影响该实例)。def test_instance_method(): obj = MyClass() with patch.object(obj, 'method', return_value='mocked_instance'): assert obj.method() == 'mocked_instance'

高级用法与注意事项
  1. 路径准确性

    patch 的路径需为被测试代码中实际导入的模块路径。例如:

    若代码中导入为 from my_module import requests,则应 patch 'my_module.requests.get' 而非 'requests.get'。

  2. 副作用处理

    return_value:固定返回值,适用于简单场景。

    side_effect:模拟异常或动态返回值。@patch('requests.get')def test_network_error(mock_get): mock_get.side_effect = Exception('Network error') with pytest.raises(Exception): fetch_data()

  3. 嵌套与多对象替换

    可同时替换多个对象,通过装饰器叠加或嵌套 with 语句:@patch('module.ClassA')@patch('module.ClassB')def test_multiple_patches(mock_b, mock_a): pass# 或使用上下文管理器def test_multiple_patches(): with patch('module.ClassA') as mock_a, patch('module.ClassB') as mock_b: pass

  4. 自动恢复与清理

    patch 会在测试结束后自动恢复原对象,无需手动清理,但需确保测试代码未意外修改全局状态。

常见问题与解决方案
  • 问题1:Mock 未生效

    原因:路径错误或未正确导入被测试对象。

    解决:检查 patch 路径是否与被测试代码中的导入路径一致。

  • 问题2:多个调用点需不同返回值

    原因:return_value 固定,无法区分调用顺序。

    解决:使用 side_effect 返回列表或函数:@patch('requests.get')def test_dynamic_responses(mock_get): mock_get.side_effect = [Mock(status_code=200), Mock(status_code=404)] assert fetch_data() == 200 # 第一次调用 assert fetch_data() == 404 # 第二次调用

  • 问题3:模拟异步函数

    原因:异步函数需返回 Coroutine 对象。

    解决:使用 AsyncMock(Python 3.8+)或手动创建协程: from unittest.mock import AsyncMock import aiohttp async def fetch_async_data(): async with aiohttp.ClientSession() as session: async with session.get('

    https://example.com'
    ) as response: return response.status @patch('aiohttp.ClientSession.get', new_callable=AsyncMock) async def test_async_fetch(mock_get): mock_get.return_value.__aenter__.return_value.status = 200 assert await fetch_async_data() == 200

总结
  • 核心优势:mock.patch 通过隔离外部依赖,使测试更快速、可靠。
  • 关键点

    路径需准确指向被测试代码中的导入名称。

    根据场景选择装饰器或上下文管理器。

    灵活使用 return_value 和 side_effect 控制行为。

  • 进阶技巧:结合 patch.object 模拟实例方法,或嵌套 patch 处理复杂依赖。

通过合理使用 mock.patch,可显著提升单元测试的质量和可维护性。