How do you mock a class in Python using Mock()
or patch()
, and which approach is more pythonic?
Method One: Directly create a Mock
object, define its behavior, and use it in the test. For example:
def test_one(self):
mock = Mock()
mock.method.return_value = True
# Call mock.method and validate the result.
self.sut.something(mock)
self.assertTrue(mock.method.called)
Method Two: Use patch
to replace the class with a mock, defining its behavior as needed. For example:
@patch("MyClass")
def test_two(self, mock):
instance = mock.return_value
instance.method.return_value = True
# Call mock.method and validate the result.
self.sut.something(instance)
self.assertTrue(instance.method.called)
Both approaches achieve similar results. Which one is better or more pythonic?
Having worked with Python for a while, I’ve seen how versatile mocking can be. The choice between mock.patch
and mock.Mock
often comes down to their specific use cases. Let’s break it down:
mock.patch
in Action
mock.patch
is perfect when you want to replace an entire class or object within a specific scope. For example, take this Python mock class:
class MyClass(object):
def method(self):
return "Original behavior"
def create_instance():
return MyClass()
Without mock.patch
:
x = create_instance()
print(x.method()) # Output: 'Original behavior'
With mock.patch
:
from unittest.mock import patch
@patch('__main__.MyClass')
def test_with_patch(MockedClass):
MockedClass.return_value.method.return_value = "Mocked behavior"
result = create_instance().method()
print(result) # Output: 'Mocked behavior'
Here, mock.patch
lets you control the behavior of MyClass
without modifying the actual class. This is crucial when testing code that internally creates new instances of a class.
mock.Mock
in Action
On the other hand, mock.Mock
creates standalone mock objects for testing specific behavior:
from unittest.mock import Mock
mock = Mock()
mock.method.return_value = "Mocked response"
assert mock.method() == "Mocked response" # True
In short, use mock.patch
when testing functions that instantiate a class internally, and use mock.Mock
when you want to pass a mock object explicitly. This distinction is key when working with a Python mock class.
@richaaroy made an excellent point about the versatility of mock.patch
. I’d like to add that sometimes you might want to mock specific methods rather than the entire Python mock class. This is where patch.object
becomes invaluable.
For instance, imagine this scenario:
class MyClass:
def method(self):
return "Original behavior"
With patch.object
, you can selectively override a method:
from unittest.mock import patch
def test_patch_object():
with patch.object(MyClass, 'method', return_value="Mocked response") as mock_method:
instance = MyClass()
result = instance.method()
assert result == "Mocked response" # True
assert mock_method.called # True
This approach is highly precise and allows you to mock only what’s necessary, leaving the rest of the class untouched. It’s a great way to maintain clarity when working with Python mock classes.
I agree with @netra.agarwal point about maintaining clarity. But there’s another approach that’s sometimes overlooked: subclassing and overriding methods in a Python mock class.
This approach lets you mock specific methods while retaining the original functionality of the class. For example:
class MyClass:
def method(self):
return "Original behavior"
class MockedMyClass(MyClass):
def method(self):
return "Mocked behavior"
def test_subclass_override():
mock_instance = MockedMyClass()
result = mock_instance.method()
assert result == "Mocked behavior" # True
By subclassing, you can mock only what’s necessary and still use the rest of the class as is. It’s particularly useful for more complex classes where you want to preserve some original functionality while testing specific behaviors.
Each method has its place, whether you’re patching, mocking, or subclassing. Choosing the right tool for the job ensures your Python mock class tests are both clean and effective.