简体   繁体   中英

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

I have a script.py file:

# 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:

# 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.

If I want to use side_effect in my @patch , I have to do things a little differently:

@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.
  • call fourth_function rather than third_function , as I cannot use any class instance variables.

What is the difference between patching in these two ways?

You don't need patches for what you are doing ( Example A ). You are passing in the parameter c which you know to be the class of BasicClass so no need for a patch. 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. If we want to simulate the return value of a function then use kwarg return_value . If we just want to mock a function, then we just use @patch without any 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. Please see Example B

==============Example 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======================

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'})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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