Re-use Patch in Python Test

Question:

Not an expert. If I patch a module’s method, is it possible to re-use the same patch in other methods of the TestCase?


    def load(**kwargs):
      return 1

    def load2(**kwargs):
      return2

    @patch.multiple('module',
                    get_data=MagicMock(side_effect=load),
                    headers=MagicMock(return_value=""))
    def test_get_some_method(self):
      # here is ok

    @patch.multiple('module',
                    get_data=MagicMock(side_effect=load2),
                    headers=MagicMock(return_value=""))
    def test_get_other_method(self):
      # here I get an exception:'load1() takes 0 positional arguments but 1 was given'

EDIT
Maybe it is better to use return_value instead of side_effect…

Asked By: gdm

||

Answers:

Yes, you can use the TestCase.setUpClass class method for this. The "patcher" returned by patch needs to be properly stopped though, if you don’t use it in the form of a decorator or context manager. Thus you should always include that call in TestCase.tearDownClass.

Here is a little demo for you.

code.py

class Spam:
    def __init__(self, x: float) -> None:
        self._x = x

    def get_x(self) -> float:
        return self._x

    def get_x_times_2(self) -> float:
        return self.get_x() * 2

    def get_x_squared(self) -> float:
        return self.get_x() ** 2

    def print_x(self) -> None:
        print(self.get_x())

Say we wanted to test all methods that call get_x and with the exact same mock object (for some reason).


test.py

from unittest import TestCase
from unittest.mock import MagicMock, patch

from . import code


class SpamTestCase(TestCase):
    get_x_patcher = None
    mock_get_x: MagicMock = None

    @classmethod
    def setUpClass(cls) -> None:
        cls.get_x_patcher = patch.object(code.Spam, "get_x")
        cls.mock_get_x = cls.get_x_patcher.start()

    @classmethod
    def tearDownClass(cls) -> None:
        cls.get_x_patcher.stop()

    def setUp(self) -> None:
        self.spam = code.Spam(3.14)

    def test_get_x_times_2(self) -> None:
        self.mock_get_x.return_value = 5
        self.assertEqual(10, self.spam.get_x_times_2())

    def test_get_x_squared(self) -> None:
        self.mock_get_x.return_value = 4
        self.assertEqual(16, self.spam.get_x_squared())

    @patch.object(code, "print")
    def test_print_x(self, mock_print: MagicMock) -> None:
        self.mock_get_x.return_value = 10.5
        self.assertIsNone(self.spam.print_x())
        mock_print.assert_called_once_with(10.5)

However, I don’t really see the use case for this. Using regular setUp and tearDown should be enough to facilitate consistency across all test methods, if you need that and don’t want to repeat yourself in multiple decorators/context managers. The mock objects will not be literally the same, but created the same way.

Hope this helps.

Answered By: Daniil Fajnberg

Based on the Daniil’answer, maybe something like this:

class TestCase(unittest.TestCase):
   def setUp(self):
        self.patcher = patch.multiple('lib.MyClass',
                                      get_data=MagicMock(side_effect=load),
                                      headers=MagicMock(return_value="")).start()

        self.my_module = MyClass()

   def test_something(self):
        _ = self.my_module.get_data()
Answered By: gdm
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.