Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›pytest & Testing›Patching — patch, patch.object

Mocking

Patching — patch, patch.object

Patching is a dynamic technique used to temporarily replace objects or methods with mock counterparts during the execution of a test. It matters because it allows developers to isolate the unit under test by stubbing out side-effect-heavy dependencies like network calls, databases, or filesystem operations. You should reach for patching whenever a function's behavior relies on an external environment that is either unpredictable, slow, or difficult to configure in a test suite.

Understanding the Patch Mechanism

At its core, patching works by manipulating the Python namespace at runtime. When you patch a target, you are essentially swapping an object reference in the module where it is being used, rather than modifying the object itself at the original source. This distinction is vital: you must always patch the object where it is looked up, not where it is defined. By temporarily injecting a Mock object into the target namespace, pytest ensures that when your code executes, it interacts with your controlled mock instead of the real dependency. Once the test completes—regardless of whether it passed or failed—the patcher automatically restores the original object to its prior state. This cleanup process is essential for maintaining test isolation, as it prevents mock leakage, where one test's changes inadvertently affect subsequent tests that rely on the actual implementation of the module.

from unittest.mock import patch
import os

def get_current_directory():
    return os.getcwd()

# We patch os.getcwd where it is imported (in this module)
def test_get_directory():
    with patch('__main__.os.getcwd', return_value='/home/user'):
        assert get_current_directory() == '/home/user'

Using patch.object for Targeted Swapping

While 'patch' takes a string path to identify the target, 'patch.object' provides a more direct approach by accepting the actual object instance itself. This method is particularly useful when you already have access to the object in the test module, allowing you to bypass the need for string path resolution entirely. By providing the host object and the attribute name, you create a cleaner and more resilient test structure that is less prone to typos in import strings. When using this, you are explicitly stating which attribute of which object needs to be replaced. This creates a tight coupling between your test setup and the specific implementation detail you intend to intercept. Because it works directly on the object reference, it is often easier to refactor, as modern IDEs can often track the attribute usage, unlike string-based paths which remain opaque to static analysis tools until execution time.

class Database:
    def connect(self):
        return "Real connection"

from unittest.mock import patch

def test_database_connection():
    db = Database()
    # Patching the 'connect' method specifically on the db instance
    with patch.object(db, 'connect', return_value="Mock connection"):
        assert db.connect() == "Mock connection"

Managing Scope and Lifetime

The lifecycle of a patch is strictly controlled by the context manager or decorator used. When a patch is used as a context manager, the replacement only exists within the indented block, which is ideal for limiting the side effects of the mock. If you prefer to use the decorator syntax, the patch is active for the entire duration of the function execution. The key takeaway here is managing state: if multiple parts of your code share a global reference, a decorator might persist longer than intended, potentially causing cascading failures in your test suite. Always prioritize the narrowest scope possible. By constraining the scope, you ensure that other tests running in the same process are not impacted by your configuration. This predictability is the foundation of a robust test suite, ensuring that your unit tests remain independent, deterministic, and resistant to the order in which they are executed by the pytest runner.

from unittest.mock import patch

@patch('os.listdir', return_value=['file1.txt'])
def test_with_decorator(mock_listdir):
    # The decorator handles setup and teardown for the entire function
    import os
    assert os.listdir('/any/path') == ['file1.txt']

Handling Return Values and Side Effects

A patch object is fundamentally a Mock, meaning you can control its behavior with extreme precision using 'return_value' or 'side_effect'. The 'return_value' property is straightforward: it returns a static value every time the mocked function is called. Conversely, the 'side_effect' property allows for dynamic behavior, such as raising exceptions or returning different values on subsequent calls. This is crucial for testing error handling logic within your code. By setting a side effect, you can simulate network timeouts, database connection failures, or invalid input scenarios that are difficult to trigger in a production environment. Mastering this distinction allows you to write tests that cover not just the 'happy path', but also the complex edge cases where your application encounters unexpected environmental issues, ensuring that your error handling routines are properly exercised and verified during your automated testing lifecycle.

from unittest.mock import patch

def test_error_handling():
    with patch('builtins.open', side_effect=FileNotFoundError):
        try:
            open('nonexistent.txt')
        except FileNotFoundError:
            assert True # Successfully simulated the error

Best Practices for Effective Mocking

To prevent tests from becoming brittle, avoid over-patching. If a test requires mocking five or more dependencies, it is a strong signal that the unit of code being tested is violating the Single Responsibility Principle and is likely too complex. Instead of mocking the world, consider refactoring your logic into smaller, more modular functions that are easier to test in isolation. When you must mock, verify your mocks using 'assert_called_with' or 'assert_called_once_with' to ensure that your code is actually interacting with the dependency as expected. This creates a contract between your implementation and the dependency. Without verification, you might be testing that your code runs, but you are failing to test whether it calls the dependency with the correct parameters, which can lead to false positives where the test passes while the integration is fundamentally broken.

from unittest.mock import patch, MagicMock

def test_verification():
    mock_func = MagicMock()
    with patch('__main__.mock_func', mock_func):
        mock_func(1, 2)
        # Ensure the dependency was called correctly
        mock_func.assert_called_once_with(1, 2)

Key points

  • Patching works by replacing object references within a specific namespace at runtime.
  • Always patch the target where the object is imported or looked up, not where it is originally defined.
  • The 'patch' function uses string paths, while 'patch.object' operates on direct object references.
  • Context managers are preferred over decorators when you need to limit the scope of a mock to a small block of code.
  • The 'return_value' attribute provides a constant result, while 'side_effect' allows for dynamic behavior like raising exceptions.
  • Properly cleaning up patches is handled automatically by the context manager or decorator, preventing state leakage between tests.
  • Over-patching is a common indicator that your code needs to be refactored into smaller, more focused units.
  • Using assertions like 'assert_called_with' is critical to verify that your code interacts with dependencies correctly.

Common mistakes

  • Mistake: Patching an object where it is defined rather than where it is used. Why it's wrong: Patching the source module doesn't affect the import in the target module. Fix: Patch the target module's reference to the object.
  • Mistake: Forgetting to stop a patch when using manual patch objects. Why it's wrong: It leaves global state mutated for subsequent tests. Fix: Use patch as a context manager or a decorator to ensure automatic cleanup.
  • Mistake: Over-mocking internal implementation details. Why it's wrong: Tests become brittle and break even when the public API remains functional. Fix: Patch only external dependencies or side-effect-heavy calls.
  • Mistake: Using patch.object on an object that hasn't been imported yet. Why it's wrong: pytest will raise an AttributeError because the target does not exist in the current scope. Fix: Ensure the module containing the object is imported before calling patch.object.
  • Mistake: Passing 'new' argument without understanding object replacement. Why it's wrong: The patch might overwrite the entire class or function with a static value, breaking expected behavior. Fix: Use a MagicMock or a specific replacement function that returns expected data.

Interview questions

What is the primary purpose of using 'patch' and 'patch.object' in pytest?

The primary purpose of using 'patch' and 'patch.object' is to isolate the unit of code under test by replacing real objects, such as external API calls, database connections, or file system interactions, with mock objects. By using these tools, you prevent your tests from relying on slow or unreliable external dependencies. This ensures that your tests remain deterministic, meaning they return the same result every time they are run, regardless of the state of the external environment or network availability.

What is the fundamental difference between 'patch' and 'patch.object'?

The fundamental difference lies in how you reference the target. 'patch' takes a string path to an object as an argument, which dynamically imports the module and replaces the target at the destination where it is imported. 'patch.object' requires the actual object or class to be passed as the first argument, followed by the name of the attribute to be replaced. Developers typically prefer 'patch.object' when they have a direct reference to the class or module, as it provides better IDE support and avoids potential issues with path-based string imports.

How does patching affect the execution of your test suite, and why is this 'mocking' considered a best practice?

Patching allows you to substitute complex parts of a system with 'Mocks' or 'MagicMocks', which record calls and return pre-defined values. This is a best practice because it forces your tests to focus strictly on the logic of the unit being tested rather than the behavior of external libraries. By decoupling your test from the underlying implementation, you gain the ability to simulate error conditions or edge cases, like a network timeout, which are difficult to trigger consistently in a real-world integration environment.

Can you compare using 'unittest.mock.patch' as a decorator versus using it as a context manager?

Using 'patch' as a decorator is often preferred for readability when the mock is needed for the entire duration of the test function, as it cleans up automatically once the function exits. However, using it as a context manager with the 'with' statement is more advantageous when you only need to mock an object for a specific segment of your code. If you only want to mock a database call inside a single loop or logic block, the context manager prevents unnecessary mocking of the setup code, keeping your test environment cleaner and more focused.

When patching, why is it critical to target the location where the object is imported rather than where it is defined?

This is a common pitfall in pytest. If you patch an object at its original definition point, the module under test might have already imported the original version. For example, if 'module_a' imports 'function_x' from 'module_b', patching 'module_b.function_x' will not affect 'module_a' because 'module_a' holds a direct reference to the original function. You must always patch the reference used within the module being tested, such as 'module_a.function_x', to ensure that your replacement is actually used by the code under test.

How would you handle a situation where you need to patch a method that is called multiple times but requires different return values in one test?

To handle multiple calls with varying returns, you can configure the 'side_effect' attribute of your mock object. Instead of setting a static 'return_value', you assign an iterable, like a list, to 'side_effect'. For instance, if you write 'mock_obj.side_effect = [1, 2, ValueError()]', the first call returns 1, the second returns 2, and the third raises a ValueError. This technique is indispensable for testing robust error handling, as it allows you to simulate a sequence of successful operations followed by an eventual system failure within a single test case.

All pytest & Testing interview questions →

Check yourself

1. You want to mock 'requests.get' inside 'myapp.api'. Which path should you provide to patch()?

  • A.patch('requests.get')
  • B.patch('myapp.api.requests.get')
  • C.patch('myapp.api.get')
  • D.patch('requests.get.myapp.api')
Show answer

B. patch('myapp.api.requests.get')
Correct: You must patch where the name is looked up, which is inside your module. Option 0 patches the original library, which won't affect the import already inside 'myapp.api'. Option 2 assumes 'get' was imported directly. Option 3 is an invalid path structure.

2. What is the primary difference between patch() and patch.object()?

  • A.patch.object is faster than patch
  • B.patch.object is only for methods, patch is for functions
  • C.patch requires a string path, patch.object takes the actual object
  • D.patch.object cannot be used as a decorator
Show answer

C. patch requires a string path, patch.object takes the actual object
Correct: patch() takes a string path to resolve the object, while patch.object takes the object instance/class directly. Option 0 is false. Option 1 is false, both can handle functions or classes. Option 3 is false, both work as decorators.

3. When using a decorator for patching, in what order are the mock objects passed to your test function?

  • A.In the order they are defined from innermost to outermost
  • B.In the order they are defined from outermost to innermost
  • C.They are not passed to the test function automatically
  • D.They are passed alphabetically by module name
Show answer

A. In the order they are defined from innermost to outermost
Correct: Decorators are applied from bottom to top, meaning the innermost decorator's mock is passed as the first argument. Option 1 is backwards. Option 2 is false as they are indeed passed. Option 3 is false.

4. Why is it recommended to use patch as a context manager if you aren't using a decorator?

  • A.It makes the test code run faster
  • B.It prevents the need for an explicit stop() call
  • C.It allows mocking of private methods
  • D.It changes the return type of the mock object
Show answer

B. It prevents the need for an explicit stop() call
Correct: Using 'with patch(...) as m:' ensures that the patch is automatically cleaned up when the block exits. Option 0 is irrelevant. Option 2 is possible with both styles. Option 3 is false.

5. If you mock a class using patch, what is the default return value when the mock is called?

  • A.A new MagicMock instance
  • B.None
  • C.The original class instance
  • D.A string representation of the class
Show answer

A. A new MagicMock instance
Correct: Mocking a class results in a MagicMock; when that mock is called (instantiated), it returns another MagicMock representing the instance. Option 1, 2, and 3 are incorrect behaviors of the mocking framework.

Take the full pytest & Testing quiz →

← Previouspytest-mock — mocker fixtureNext →Side Effects and Return Values

pytest & Testing

25 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app