简体   繁体   中英

Patch a single class method in a unittest and assert it is called once

How do you patch a method in a class and then assert that that patched method was only called once?

For example:

import typing
import unittest
import unittest.mock

class Example:
    def __init__(self: "Example") -> None:
        self._loaded : bool = False
        self._data : typing.Union[str,None] = None

    def data(self: "Example") -> str:
        if not self._loaded:
            self.load()
        return self._data

    def load(self: "Example") -> None:
        self._loaded = True
        # some expensive computations
        self._data = "real_data"

def mocked_load(self: "Example") -> None:
    # mock the side effects of load without the expensive computation.
    self._loaded = True
    self._data = "test_data"

class TestExample( unittest.TestCase ):
    def test_load(self: "TestExample") -> None:
        # tests for the expensive computations
        ...

    @unittest.mock.patch("__main__.Example.load", new=mocked_load)
    def test_data(
        self: "TestExample",
        # patched_mocked_load: unittest.mock.Mock
    ) -> None:
        example = Example()
        data1 = example.data()
        self.assertEqual(data1, "test_data")
        # example.load.assert_called_once()
        # Example.load.assert_called_once()
        # mocked_load.assert_called_once()
        # patched_mocked_load.assert_called_once()

        data2 = example.data()
        self.assertEqual(data2, "test_data")
        # Should still only have loaded once.
        # example.load.assert_called_once()
        # Example.load.assert_called_once()
        # mocked_load.assert_called_once()
        # patched_mocked_load.assert_called_once()

if __name__ == '__main__':
    unittest.main()

How in the test_data unit test do I assert that the patched load function was only called once?

The problem is that you substitute load with your own function, which is not a mock and therefore does not have assert_called_xxx methods.
You need to substitute it with a mock and add the wanted behavior to the mock instead:

    @unittest.mock.patch("__main__.Example.load")
    def test_data(
            self: "TestExample",
            patched_mocked_load: unittest.mock.Mock
    ) -> None:
        example = Example()
        patched_mocked_load.side_effect = lambda: mocked_load(example)
        data1 = example.data()
        self.assertEqual(data1, "test_data")
        patched_mocked_load.assert_called_once()

        data2 = example.data()
        self.assertEqual(data2, "test_data")
        patched_mocked_load.assert_called_once()

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