likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 1

前言

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

pytest-mock是啥?

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

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

pip install pytest-mock

基本用法

模拟函数的返回值

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

# 测试函数
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参数来实现这一点。以下是一个示例:

# 测试函数
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还可以模拟对象的属性。以下是一个示例:

# 测试函数
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

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_additiontest_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)进行乘法运算,并使用断言验证结果是否正确。

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

pytest -s -v test_demo.py::test_addition

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

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进行单元测试。掌握了这些基本用法,可以更轻松地编写可靠的单元测试。