深入探索pytest-mock插件:简化Python单元测试中的模拟和替换

前言

进行接口测试时,可能有些接口逻辑未实现或者不满足要求,我们需要先调试case的逻辑,此时可能就需要mock了。那在pytest中该如何进行mock呢?带着这个疑问我们一起探索。

pytest-mock是啥?

pytest-mock是一个pytest的插件,安装即可使用。它提供了一个名为mocker的fixture,仅在当前测试function或method生效,而不用自行包装。

首先,我们需要安装pytest-mock库。可以使用pip命令来安装:

复制代码
pip install pytest-mock

基本用法

模拟函数的返回值

有时我们需要模拟一个函数的返回值,以便更好地控制测试场景。pytest-mock可以通过mocker夹具实现这一点。以下是一个示例:

ini 复制代码
# 测试函数
def test_function(mocker):
    # 模拟一个函数的返回值
    mocker.patch('module.function', return_value=42)
    
    result = module.function()
    
    assert result == 42

在上面的示例中,我们使用mocker.patch方法模拟了module.function函数的返回值为42。然后,我们调用该函数,并断言返回的结果是否为42。

模拟函数的行为

有时我们需要模拟一个函数的行为,例如抛出异常或调用其他函数。pytest-mock提供了side_effect参数来实现这一点。以下是一个示例:

scss 复制代码
# 测试函数
def test_function(mocker):
    # 模拟一个函数的行为,抛出异常
    mocker.patch('module.function', side_effect=Exception('mocked error'))
    
    with pytest.raises(Exception):
        module.function()

在上面的示例中,我们使用mocker.patch方法模拟了module.function函数抛出异常。然后,我们使用pytest.raises上下文管理器来捕获预期的异常。

模拟对象的属性

除了模拟函数的返回值和行为,pytest-mock还可以模拟对象的属性。以下是一个示例:

ini 复制代码
# 测试函数
def test_function(mocker):
    # 创建一个模拟对象
    obj = mocker.MagicMock()
    # 设置模拟对象的属性
    obj.attribute = 10
    
    assert obj.attribute == 10

在上面的示例中,我们使用mocker.MagicMock创建了一个模拟对象,并通过设置obj.attribute属性为10来模拟对象的属性。然后,我们断言该属性的值是否为10。

场景案例

一个简单的计算器类Calculator,该类提供了加法和乘法运算的方法。我们将使用pytest-mock来模拟依赖项并针对这个模块编写单元测试。

test_demo.py

python 复制代码
class Calculator:
    def add(self, x, y):
        pass
​
    def multiply(self, x, y):
        pass
​
​
# 测试计算器类的加法运算
def test_addition(mocker):
    # 创建一个模拟对象
    calculator = Calculator()
​
    # 模拟add方法的行为
    mocker.patch.object(calculator, 'add', return_value=5)
​
    # 调用add方法
    result = calculator.add(2, 3)
​
    # 断言结果是否为模拟的返回值
    assert result == 5
​
​
# 测试计算器类的乘法运算
def test_multiplication(mocker):
    # 创建一个模拟对象
    calculator = Calculator()
​
    # 模拟multiply方法的行为
    mocker.patch.object(calculator, 'multiply', side_effect=lambda x, y: x * y)
​
    # 调用multiply方法
    result = calculator.multiply(2, 3)
​
    # 断言结果是否为模拟的返回值
    assert result == 6
​

在上述代码中,我们定义了两个测试函数test_addition和test_multiplication,分别测试了计算器类的加法运算和乘法运算。

对于测试加法运算的函数test_addition,我们创建了一个名为calculator的模拟对象,并使用mocker.patch.object方法模拟了calculator.add方法的返回值为5。然后,我们调用calculator.add(2, 3)进行加法运算,并使用断言验证结果是否为模拟的返回值。

对于测试乘法运算的函数test_multiplication,我们也创建了一个名为calculator的模拟对象,并使用mocker.patch.object方法模拟了calculator.multiply方法的行为,即通过lambda函数实现了真实的乘法运算。然后,我们调用calculator.multiply(2, 3)进行乘法运算,并使用断言验证结果是否正确。

运行测试: 执行以下命令来运行测试:

arduino 复制代码
pytest -s -v test_demo.py::test_addition

执行之后会输出PASSED,我们如果改动mock值mocker.patch.object(calculator, 'add', return_value=51),再次执行命令,会输出如下结果:

markdown 复制代码
FAILED
​
=============================================================== FAILURES ===============================================================
____________________________________________________________ test_addition _____________________________________________________________
​
mocker = <pytest_mock.plugin.MockerFixture object at 0x11140a250>
​
    def test_addition(mocker):
        # 创建一个模拟对象
        calculator = Calculator()
​
        # 模拟add方法的行为
        mocker.patch.object(calculator, 'add', return_value=51)
​
        # 调用add方法
        result = calculator.add(2, 3)
​
        # 断言结果是否为模拟的返回值
>       assert result == 5
E       assert 51 == 5
​
test_dir/test_demo.py:24: AssertionError

相信到这里你应该已经掌握的它的用法。

运行机制

pytest-mock的运行机制如下:

  1. pytest-mock的mock功能,可以用来修改、替换或者移除原有的Python对象。
  2. 在测试函数中,如果需要mock某个函数或对象的行为,可以在测试函数参数中添加mocker参数,mocker是一个Fixture对象,可以用来创建并管理mock对象。
  3. 通过调用mocker.patch()方法,可以对要mock的函数或对象进行替换或者修改。例如,在测试函数中,调用mocker.patch()方法,并传入要mock的函数名以及mock后的返回值,就可以将该函数的实际调用结果替换成mock的返回值,以便测试函数的正确性。

最后

本文介绍了pytest-mock的基本用法,包括模拟函数的返回值和行为,以及模拟对象的属性。我们还通过一个案例说明展示了如何使用pytest-mock进行单元测试。掌握了这些基本用法,可以更轻松地编写可靠的单元测试。

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
默_笙3 天前
🍙 给每个请求过安检:FastAPI 是怎么把校验写进类型注解的
python
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
qq_426003963 天前
启动playwright录制codegen生成自动化测试脚本
python·自动化
虎头金猫3 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
胡写代码3 天前
别再前后端各写一套表单校验了
java·后端
长沙三为智能科技3 天前
家政小程序开发从0到上线:五阶段交付流程与验收清单
python
伞伞悦读3 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
大勇前进3 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端