简体   繁体   English

使用带有和不带有 side_effect 的 @patch() 模拟有什么区别?

[英]What is the difference between mocking with @patch() with and without side_effect?

I have a script.py file:我有一个script.py文件:

# in script.py
def my_basic_function(value, c):
    return c(value).words()
class BasicClass:
    def __init__(self, val):
        self.val = val
    def words():
        return self.val

and a test.py file:和一个test.py文件:

# in test.py
from mock import patch
from script import my_basic_function, BasicClass

def third_function(self,):
    return "not {}".format(self.val)

def fourth_function():
    return "not ponies"

def init_mock(self, val):
    self.val = val

@patch('script.BasicClass.words', third_function)
@patch('script.BasicClass.__init__', init_mock)
def test_my_basic_function():
    assert my_basic_function("ponies", BasicClass) == "not ponies"

that I can run using pytest test.py from the command line successfully.我可以成功地从命令行使用pytest test.py运行。

If I want to use side_effect in my @patch , I have to do things a little differently:如果我想在我的@patch使用side_effect ,我必须做一些不同的事情:

@patch('script.BasicClass.words', side_effect = fourth_function)
@patch('script.BasicClass.__init__', init_mock)
def test_my_basic_function(amock):
    assert my_basic_function("ponies", BasicClass) == "not ponies"

ie, I have to:即,我必须:

  • add an argument to test_my_basic_function that I never use.test_my_basic_function添加一个我从未使用过的参数。
  • call fourth_function rather than third_function , as I cannot use any class instance variables.调用fourth_function而不是third_function ,因为我不能使用任何类实例变量。

What is the difference between patching in these two ways?这两种方式打补丁有什么区别?

You don't need patches for what you are doing ( Example A ).您不需要为您正在做的事情打补丁(示例 A )。 You are passing in the parameter c which you know to be the class of BasicClass so no need for a patch.您正在传入参数 c,您知道它是 BasicClass 的类,因此不需要补丁。 You would need a patch if you introduced a function call or object initialization in the function you are testing where you don't want to actually take place.如果您在正在测试的函数中引入了函数调用或对象初始化,而您不想实际发生,则需要一个补丁。

When using @patch we should use the kwarg side_effect to simulate the raising of exceptions, not calling other functions.使用@patch我们应该使用 kwarg side_effect来模拟引发异常,而不是调用其他函数。 If we want to simulate the return value of a function then use kwarg return_value .如果我们想模拟函数的返回值,请使用 kwarg return_value If we just want to mock a function, then we just use @patch without any kwargs.如果我们只想模拟一个函数,那么我们只需使用@patch而不使用任何 kwargs。 When we use patches as a decorator we need to pass them into the function.当我们使用补丁作为装饰器时,我们需要将它们传递给函数。 It is true they can be unused but we should use them with mock functions such as assert_called_once or assert_called_once_with to ensure your patches are working as expected.的确,它们可以未被使用,但我们应该将它们与诸如assert_called_onceassert_called_once_with类的模拟函数一起使用,以确保您的补丁按预期工作。 Please see Example B请参见示例 B

==============Example A================== ==============示例A==================

import unittest


def my_basic_function(value, c):
    return c(value).words()


class BasicClass:
    def __init__(self, val):
        self.val = val

    def words(self):
        return self.val


class TestMyBasicFunction(unittest.TestCase):

    def test_my_basic_class(self):
        value = my_basic_function("jeremy", BasicClass)
        self.assertEqual("jeremy", value)

    def test_my_basic_class_wrong(self):
        value = my_basic_function("jeremy", BasicClass)
        self.assertNotEqual("w33b", value)

============Example B====================== ============示例B======================

import unittest
from unittest.mock import patch


def ensure_str(value):
    try:
        return str(value)
    except Exception:
        raise TypeError


def my_basic_function(value, c):
    value = ensure_str(value)
    return c(value).words()


class BasicClass:
    def __init__(self, val):
        self.val = val

    def words(self):
        return self.val


class TestMyBasicFunction(unittest.TestCase):

    @patch('script.ensure_str', return_value="jeremy")
    def test_return_value(self, ensure_str_mock):
        value = my_basic_function("jeremy", BasicClass)
        ensure_str_mock.assert_called_once_with("jeremy")
        self.assertEqual("jeremy", value)

    @patch('script.ensure_str')
    def test_no_return_value(self, ensure_str_mock):
        value = my_basic_function("jeremy", BasicClass)
        self.assertEqual(ensure_str_mock(), value)

    @patch('script.ensure_str', side_effect=TypeError)
    def test_side_effect(self, ensure_str_mock):
        with self.assertRaises(TypeError):
            value = my_basic_function({'apple': 'sauce'}, BasicClass)
        ensure_str_mock.assert_called_once_with({'apple': 'sauce'})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM