Which Python mocking method is more Pythonic: Mock or patch?

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.