pytest-mock深度解析

以下是关于 pytest-mock 的深度解析,涵盖核心功能、最佳实践和高级用法,帮助您高效利用 Mock 进行单元测试:

一、pytest-mock 的核心优势

1. 与传统 unittest.mock 对比

集成度

需手动导入

通过

mocker

fixture 自动注入

作用域管理

需手动

patch.stop()

自动清理 Mock(函数作用域)

Pytest 生态兼容

部分功能冲突

原生支持 Pytest 插件体系

代码简洁性

较高冗余

更简洁的链式调用

2. 安装与基础使用

pip install pytest-mock

def test_with_mock(mocker):  # 自动注入 mocker fixture
    mock_obj = mocker.patch("module.ClassName")
    mock_obj.method.return_value = "fake_result"
    assert module.ClassName().method() == "fake_result"

二、核心 Mock 技术详解

1. 模拟对象方法

# 模拟 requests.get()
def test_api_call(mocker):
    mock_get = mocker.patch("requests.get")
    mock_get.return_value.json.return_value = {"key": "value"}
    
    response = my_module.fetch_data()
    assert response["key"] == "value"
    mock_get.assert_called_once_with("https://api.example.com")

2. 模拟属性

class User:
    name = "Alice"

def test_property(mocker):
    mocker.patch.object(User, "name", "Bob")
    assert User.name == "Bob"  # 临时修改类属性

3. 模拟上下文管理器

def test_context_manager(mocker):
    mock_open = mocker.patch("builtins.open", mocker.mock_open(read_data="content"))
    with open("file.txt") as f:
        assert f.read() == "content"

4. 模拟异常

def test_exception(mocker):
    mocker.patch("module.func", side_effect=ValueError("error"))
    with pytest.raises(ValueError, match="error"):
        module.func()

三、高级应用场景

1. 验证调用参数

def test_args_verification(mocker):
    mock_send = mocker.patch("module.send_email")
    module.notify_user("**********")
    
    # 验证调用参数
    mock_send.assert_called_once_with(
        to="**********",
        subject="Notification",
        body=mocker.ANY  # 通配符匹配
    )

2. 动态返回值

def test_dynamic_return(mocker):
    mock_db = mocker.patch("module.Database.query")
    mock_db.side_effect = [
        ["result1", "result2"],  # 第一次调用返回
        [],                      # 第二次调用返回
        Exception("DB error")    # 第三次抛出异常
    ]
    
    assert module.get_data() == ["result1", "result2"]
    assert module.get_data() == []
    with pytest.raises(Exception):
        module.get_data()

3. Spy 对象(部分 Mock)

def test_spy(mocker):
    real_func = module.original_func  # 保存原函数
    spy = mocker.spy(module, "original_func")
    
    result = module.wrapper_func()
    assert spy.call_count == 1
    assert spy.return_value == result
    assert real_func() == result  # 原功能仍可用

四、最佳实践与陷阱规避

1. Mock 作用域控制

function

(默认)

单个测试函数内有效

大多数情况

module

整个测试模块有效

共享昂贵 Mock 初始化

session

全局有效(慎用)

极少数全局替换

@pytest.fixture(scope="module")
def mock_db(mocker):
    return mocker.patch("module.Database", autospec=True)

2. 自动规范(autospec)

mocker.patch("module.Class", autospec=True)  # 严格校验方法是否存在
mocker.patch.object(real_obj, "method", autospec=True)  # 校验参数类型

3. 常见陷阱

  • 错误1:Mock 导入路径不一致
  • 错误2:过早 Mock

五、与其他工具集成

1. pytest-cov 配合

# 确保 Mock 不影响覆盖率统计
pytest --cov=my_module --cov-branch tests/

2. 参数化测试结合

@pytest.mark.parametrize("input,expected", [(1, 2), (3, 4)])
def test_parametrized(mocker, input, expected):
    mocker.patch("module.transform", return_value=expected)
    assert module.process(input) == expected

3. 异步代码 Mock

import pytest

@pytest.mark.asyncio
async def test_async_mock(mocker):
    mock_func = mocker.patch("module.async_func", return_value="result")
    assert await module.call_async() == "result"
    mock_func.assert_awaited_once()

六、性能优化

1. Mock 复用

@pytest.fixture
def mock_service(mocker):
    return mocker.patch("module.ExternalService", autospec=True)

def test_case1(mock_service):
    mock_service.call.return_value = "response1"
    ...

def test_case2(mock_service):
    mock_service.call.return_value = "response2"
    ...

2. 轻量级 Mock

# 使用 MagicMock 替代真实对象
def test_lightweight(mocker):
    mock = mocker.MagicMock(spec=RealClass)  # 不触发真实初始化
    mock.method.return_value = 42
    ...

七、调试技巧

1. 打印 Mock 调用记录

def test_debug(mocker):
    mock = mocker.patch("module.func")
    module.run_logic()
    print(mock.mock_calls)  # 查看所有调用记录

2. Mock 未生效排查

  1. 检查 mocker.patch 路径是否与被测代码导入路径完全一致
  2. 确认 Mock 在代码执行前已生效
  3. 使用 pytest --pdb 进入调试模式检查

通过掌握这些技术,您可以:✅ 精确隔离测试目标

✅ 模拟各种复杂依赖(网络、数据库等)

✅ 验证系统交互行为

✅ 构建快速稳定的单元测试套件

进阶高级测试工程师 文章被收录于专栏

《高级软件测试工程师》专栏旨在为测试领域的从业者提供深入的知识和实践指导,帮助大家从基础的测试技能迈向高级测试专家的行列。 在本专栏中,主要涵盖的内容: 1. 如何设计和实施高效的测试策略; 2. 掌握自动化测试、性能测试和安全测试的核心技术; 3. 深入理解测试驱动开发(TDD)和行为驱动开发(BDD)的实践方法; 4. 测试团队的管理和协作能力。 ——For.Heart

全部评论

相关推荐

求面试求offer啊啊啊啊:1600一个月?
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务