Python Pytest:使用无副作用的函数设置模拟

Python Pytest:使用无副作用的函数设置模拟,python,mocking,pytest,Python,Mocking,Pytest,关于这个问题的答案,我有一个问题: 我喜欢使用通过参数接收模拟对象的函数。这样,可以重用模拟的设置。我从答案中得出结论,在Python中模拟对象是可变的,在函数内部更改它们将产生副作用,即它们在外部更改。但是,我认为副作用是危险的。因此,我建议如下: def test(self, mock1): mock1 = setup_mock1_to_always_xxx(mock1) ... 与 这可能吗?我建议使用注入模拟,而不是手动模拟复制。功能范围内的装置(默认装置)将为每个测

关于这个问题的答案,我有一个问题:

我喜欢使用通过参数接收模拟对象的函数。这样,可以重用模拟的设置。我从答案中得出结论,在Python中模拟对象是可变的,在函数内部更改它们将产生副作用,即它们在外部更改。但是,我认为副作用是危险的。因此,我建议如下:

def test(self, mock1):
    mock1 = setup_mock1_to_always_xxx(mock1)
    ...


这可能吗?

我建议使用注入模拟,而不是手动模拟复制。功能范围内的装置(默认装置)将为每个测试重新评估。示例:假设您有一个模块

# mod.py

def spam():
    return eggs()


def eggs():
    return "eggs"
还有一个测试

from unittest.mock import patch
from mod import spam


@patch("mod.eggs")
def test_bacon(mock1):
    mock1.return_value = "bacon"
    assert spam() == "bacon"
现在,您需要将其重构为针对
培根
培根鸡蛋
进行测试。移出夹具内的修补:

import pytest
from unittest.mock import patch
from mod import spam


@pytest.fixture
def eggs_mock():
    with patch("mod.eggs") as mock1:
        yield mock1


def test_bacon(eggs_mock):
    eggs_mock.return_value = "bacon"
    assert spam() == "bacon"


def test_bacon_with_eggs(eggs_mock):
    eggs_mock.return_value = "bacon with eggs"
    assert spam() == "bacon with eggs"
现在有两个不同的
mod.eggs
函数模拟,每个测试中有一个唯一的模拟

unittest
-样式测试 这种方法也适用于
unittest
测试类,尽管注入有点冗长,因为
unittest.TestCase
s不接受测试方法中的参数。这与中描述的方法相同。在下面的示例中,我通过使用一个附加属性将
eggs\u mock
fixture返回值存储在
Tests
实例属性中:


只是设置另一个模拟而不是复制不是更容易吗<在您的示例中,code>mock1可以是一个fixture,它的函数作用域将在执行每个测试之前被调用,因此每个测试都会得到一个新的mock。@hoefling,我认为这是最优雅的答案。你想从你的评论中做出一个回答吗,也许可以举个例子?我不想拿你的学分,否则我会做的。
import pytest
from unittest.mock import patch
from mod import spam


@pytest.fixture
def eggs_mock():
    with patch("mod.eggs") as mock1:
        yield mock1


def test_bacon(eggs_mock):
    eggs_mock.return_value = "bacon"
    assert spam() == "bacon"


def test_bacon_with_eggs(eggs_mock):
    eggs_mock.return_value = "bacon with eggs"
    assert spam() == "bacon with eggs"
from unittest import TestCase
from unittest.mock import patch
import pytest
from mod import spam


@pytest.fixture
def eggs_mock():
    with patch("mod.eggs") as mock1:
        yield mock1


class Tests(TestCase):

    @pytest.fixture(autouse=True)
    def inject_eggs_mock(self, eggs_mock):
        self._eggs_mock = eggs_mock

    def test_bacon(self):
        self._eggs_mock.return_value = "bacon"
        assert spam() == "bacon"


    def test_bacon_with_eggs(self):
        self._eggs_mock.return_value = "bacon with eggs"
        assert spam() == "bacon with eggs"