When mocking classes or methods when writing unittests in Python, why do I need to use @patch decorator? I just could replace the method with Mock object without any patch annotation.
Examples:
class TestFoobar(unittest.TestCase):
def setUp(self):
self.foobar = FooBar()
# 1) With patch decorator:
@patch.object(FooBar, "_get_bar")
@patch.object(FooBar, "_get_foo")
def test_get_foobar_with_patch(self, mock_get_foo, mock_get_bar):
mock_get_bar.return_value = "bar1"
mock_get_foo.return_value = "foo1"
actual = self.foobar.get_foobar()
self.assertEqual("foo1bar1", actual)
# 2) Just replacing the real methods with Mock with proper return_value:
def test_get_foobar_with_replacement(self):
self.foobar._get_foo = Mock(return_value="foo2")
self.foobar._get_bar = Mock(return_value="bar2")
actual = self.foobar.get_foobar()
self.assertEqual("foo2bar2", actual)
Could someone produce an example, where patch decorator is good and replacing is bad?
We have always used patch decorator with our team, but after reading this comment for a post, I got the idea that maybe we could write nicer-looking code without the need of patch decorators.
I understand that patching is temporary, so maybe with some cases, it is dangerous to not use patch decorator and replace methods with mock instead? Could it be that replacing objects in one test method can affect the result of the next test method?
I tried to prove this, but came up empty: both tests pass in the next code:
def test_get_foobar_with_replacement(self):
self.foobar._get_foo = Mock(return_value="foo2")
self.foobar._get_bar = Mock(return_value="bar2")
actual = self.foobar.get_foobar()
self.assertIsInstance(self.foobar._get_bar, Mock)
self.assertIsInstance(self.foobar._get_foo, Mock)
self.assertEqual("foo2bar2", actual)
def test_get_foobar_with_real_methods(self):
actual = self.foobar.get_foobar()
self.assertNotIsInstance(self.foobar._get_bar, Mock)
self.assertNotIsInstance(self.foobar._get_foo, Mock)
self.assertIsInstance(self.foobar._get_bar, types.MethodType)
self.assertIsInstance(self.foobar._get_foo, types.MethodType)
self.assertEqual("foobar", actual)
Full source code (Python 3.3): dropbox.com/s/t8bewsdaalzrgke/test_foobar.py?dl=0
patch.object
will restore the item you patched to its original state after the test method returns. If you monkey-patch the object yourself, you need to restore the original value if that object will be used in another test.
In your two examples, you are actually patching two different things. Your call to patch.object
patches the class FooBar
, while your monkey patch patches a specific instance of FooBar
.
Restoring the original object isn't important if the object will be created from scratch each time. (You don't show it, but I assume self.foobar
is being created in a setUp
method, so that even though you replace its _get_foo
method, you aren't reusing that specific object in multiple tests.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With