tl;dr how to have a mock.patch for a @classmethod last an entire test session instead of only within with scope or function scope?
I want to mock patch a class method. However, I would like to run patch.object once instead of once within every test.
Currently, I must call patch.object twice
class MyClass():
@classmethod
def print_hello(cls):
print("hello from the real MyClass.print_hello")
def do_something(self):
pass
def mock_print_hello(_cls):
print("hello from the patched mock_print_hello")
class TestMyClass(unittest.TestCase):
def test_init(self):
with mock.patch.object(MyClass, "print_hello", new_callable=mock_print_hello) as patch:
MyClass.print_hello()
MyClass()
def test_do_something(self):
with mock.patch.object(MyClass, "print_hello", new_callable=mock_print_hello) as patch:
MyClass.print_hello()
MyClass().do_something()
However, I would like to only call patch.object once:
class MyClass():
@classmethod
def print_hello(cls) :
print("hello from the real MyClass.print_hello")
def do_something(self):
pass
def mock_print_hello(_cls):
print("hello from the patched mock_print_hello")
class TestMyClass(unittest.TestCase):
@classmethod
def setUpClass(cls):
# this patch will not remain after setUpClass returns
patch = mock.patch.object(MyClass, "print_hello", new_callable=mock_print_hello)
def test_init(self):
# this calls the real MyClass.print_hello, not mock_print_hello
MyClass.print_hello()
MyClass()
def test_do_something(self):
# this calls the real MyClass.print_hello, not mock_print_hello
MyClass.print_hello()
MyClass().do_something()
In practice, patching MyClass.new within setUpClass does not last beyond the scope of the function. In other words, the test_init and test_do_something do not call mock_print_hello, they call MyClass.print_hello.
How can I have the mock patch last beyond the scope of one function?
In order to start patching, you have to either use a decorator or a context manager, or you can call start manually. Just calling
patchorpatch.objectreturns a patcher object, it does not start patching.As you can't use the decorator or context manager in your setup code, you have to start/stop the patcher in the respective setUp/tearDown methods:
If you need the patching to persist in the whole module, you can do the same in the respective module functions:
As an aside, if you were to use
pytestinstead ofunittest, this could also be done using a context manager in a fixture:or in the module case: