Python

P
PythonHub
Progress 0%

Python

  • Home
  • History of Python
  • Applications of Python
  • Introduction To Python
    • What is Python
    • Character Set
    • Tokens in Python
    • Python Execution Mode
    • Variable And Identifiers
    • Data Types in Python
    • Operators And Expressions
    • Constants in Python
    • Assignment Statement
    • Input / Output in Python
    • Simple Python Scripts
    • Namespace in Python
    • 📝 Assignments
  • Operators in Python
    • Arithmetic Operators
    • Assignment Operators
    • Shorthand Assignment Operators
    • Relational Operators
    • Logical Operators
    • Bitwise Operators
    • Special Operators
    • 📝 Assignments
  • Input Output in Python
    • Accept Input
    • Output Formatting
    • 📝 Assignments
  • Conditional Statement
    • Decision Making
    • if Statement
    • IF-ELSE STATEMENT
    • IF-ELSE LADDER
    • NESTED IF-ELSE
    • Short Hand IF-ELSE
    • 📝 Assignments
  • Loops
    • Introduction to Loops
    • While Loop
    • Nested While Loop
    • 📝 While Loop Assignments
    • For Loop
    • For Loop Examples
    • Nested For Loop
    • Nested For Loop Examples
    • Infinite While Loops
    • Infinite For Loops
    • Break, Continue and Else in Loops
    • Difference Between For and While Loop
    • For Each Loop
    • 📝 For Each Assignments
    • 📝 All Loops Assignments
  • List
    • List in Python
    • Access List Elements
    • List Functions
    • Iterate (Loop) List
    • List Comprehension
    • 📝 Assignments
  • Tuple
    • Tuple in Python
    • Access Tuple Elements
    • Tuple Functions
    • Iterate (Loop) Tuple
    • Unpack Tuple
    • Tuple Comprehension
    • 📝 Assignments
  • Set
    • Set in Python
    • Access Set Elements
    • Set Methods
    • Iterate (Loop) Set
    • Pack/Unpack Set
    • Set Comprehension
    • 📝 Assignments
  • Dictionary
    • Dictionary
    • Access Dictionary Items
    • Dictionary Methods
    • Iterate (Loop) Dictionary
    • Formatting Dictionaries
    • Nested Dictionaries
    • Dictionary Comprehension
    • 📝 Assignments
  • Diff List Tuple Set Dictionary
    • List vs Tuple
    • List vs Set
    • List vs Dictionary
    • Tuple vs Set
    • Tuple vs Dictionary
    • Dictionary vs Set
    • 📝 Assignments
  • Exception
    • Error vs Exception
    • Types of Exception
    • Exception Handling
    • User Defined Exception
    • Logging Exception
    • 📝 Assignments
  • Functions
    • Introduction to Functions
    • Modular Programming
    • Types of Functions
    • Inbuilt Functions
    • Need For User-Defined Function
    • Elements of User Defined Function
    • Function Arguments
    • Nesting of Functions
    • Recursion
    • Global Local and Non Local
    • Python Lambda Functions
    • 📝 Assignments
  • Python Module
    • Introduction to Module
    • Inbuilt Modules in Python
    • User Defined Module
    • 📝 Assignments
  • File Handling
    • Introduction to Files
    • Create File
    • Read Files
    • Write to File
    • Rename File
    • Copy File
    • Move File
    • List Files in Directory
    • Binary Files
    • Zipping and Unzipping Files
    • 📝 Assignments
  • Strings
    • Basics of Strings
    • String Special Operators
    • String Formatting Operators
    • String Methods
    • 📝 Assignments
  • Regular Expressions
  • Python OOPS
    • Basics of Object Oriented
    • What are Classes and Objects?
    • Creating Class and Object
    • OOP vs Procedural Programming
    • Difference Between Classes and Objects
    • Constructors
    • Destructor
    • Built Class Methods and Attributes
    • Class and Instance Variables
    • Inheritance in Python
    • Single Inheritance
    • Multiple Inheritance
    • Multilevel Inheritance
    • Hierarchical Inheritance
    • Hybrid Inheritance
    • Abstraction
    • Method Overriding
    • Abstract Method
    • Interfaces in Python
    • Abstract Class vs Interface
    • Public, Private and Protected
    • Overloading vs Overriding
    • Inheritance vs Composition
    • Encapsulation
    • Polymorphism
    • Inner Classes
    • 📝 Assignments
  • Advanced Python
    • Iterator in Python
    • Generator in Python
    • Decorator in Python
    • Type Hints in Python
    • Async/Await Programming
    • Dataclasses in Python
    • Context Managers in Python
    • Property Decorator in Python
    • __slots__ in Python
    • Enums in Python
    • Walrus Operator in Python
    • Match-Case in Python
    • 📝 Assignments
  • Python Standard Library
    • Collections Module
    • Itertools Module
    • Functools Module
    • Datetime Module
    • JSON Module
    • OS Module
    • Sys Module
    • Random Module
    • Math Module
    • 📝 Assignments
  • Python Testing
    • Unit Testing in Python
    • Pytest Framework
    • Mocking in Python
    • 📝 Assignments
  • Python Best Practices
    • PEP 8 Style Guide
    • Docstrings in Python
    • Logging in Python
    • Code Optimization Tips
    • Debugging Techniques
    • 📝 Assignments
  • MySQL Database in Python
    • Introduction to MySQL with Python
    • DBMS vs File System
    • Connecting to MySQL Database
    • Create Database in MySQL
    • Create Table in MySQL
    • Insert Data in MySQL
    • Insert Multiple Rows
    • Select Data from MySQL
    • WHERE Clause in MySQL
    • Update Data in MySQL
    • Delete Data from MySQL
    • Parameterized Queries
    • Transaction Management
    • Error Handling
    • Connection Pooling
    • MySQL Drivers Guide
    • Joins in MySQL
    • Aggregation Functions
    • Backup MySQL Database
    • Best Practices
    • 📝 Assignments
  • MySQL Database Operations
    • SELECT Statement
    • MySQL Operators
    • DDL Statements
    • DML Statements
    • Subqueries
    • JOIN Operations
    • Aggregation
    • Case Study
    • 📝 Assignments
  • Graphics in Python
  • Threads in Python
    • Introduction to Threads
    • Process vs Threads
    • Concurrent Programming & GIL
    • Uses of Threads
    • Creating Threads
    • Single Tasking
    • Multi Tasking
    • Thread Synchronization
    • 📝 Assignments
  • Interview Questions & Answers
  • Python Case Studies
  • Multiple Choice Questions
  • 📝 Practice Problems
Get in Touch
  • tech2dsm@gmail.com

© Sankalan Data Tech

Python Language Interactive Tutorial

Python: Mocking

Python Mocking - Complete Guide

Test your code in isolation by replacing dependencies with mocks.

Created by Sankalan Data Tech Team Verified
Data Engineers, Analysts, Scientists & Trainers
Created by experienced Python developers, data engineers, and data scientists to make programming easy through practical examples, real-world experience, and clear explanations.
On this page:
  • What is Mocking?
  • Why Use Mocks?
  • Mock Objects
  • Patching with patch
  • Mock Assertions
  • Real-World Example
  • Best Practices
  • Try It Yourself
  • Quick Quiz
  • Frequently Asked Questions
Share this tutorial:
Twitter LinkedIn Facebook WhatsApp Reddit Telegram Email Copy Link
What You'll Learn Here
  • What is mocking — replacing real objects with fake ones for testing
  • Why use mocks — test code in isolation, speed up tests
  • Mock objects — creating and configuring mocks
  • Patching — replacing functions and classes temporarily
  • Assertions — verify how mocks were used
  • Real-world examples — mocking APIs, databases, and more

What is Mocking?

Mocking is the practice of replacing real objects with fake ones during testing. A mock object simulates the behavior of a real object so you can test your code without relying on external dependencies.

Think of mocking like a movie stunt double. When a movie needs a dangerous scene, they use a stunt double instead of the real actor. The stunt double looks and acts like the actor, but they're safer and more controllable. In testing, mocks are like stunt doubles for your code's dependencies.

Python provides the unittest.mock module for mocking. It's part of the standard library and works with both unittest and pytest.

💡 Key concept: Mocking lets you test your code in isolation by replacing real dependencies with controlled fake objects.

Why Use Mocks?

1

The Benefits of Mocking

Mocking makes your tests faster, more reliable, and easier to write.

# Why Use Mocks?

print("=" * 50)
print("WHY USE MOCKS?")
print("=" * 50)

# ============================================================
# WITHOUT MOCKS - Testing with Real Dependencies
# ============================================================

print("\n1. WITHOUT MOCKS")

print("""
# This code makes a real API call
import requests

def get_user_data(user_id):
    response = requests.get(f"https://api.example.com/users/{user_id}")
    return response.json()

def test_get_user_data():
    # This makes a real API call!
    user = get_user_data(123)
    assert user["id"] == 123

Problems:
- Slow (network call)
- Unreliable (API might be down)
- Requires internet
- Costs money (API usage)
- Can't test error cases easily
""")


# ============================================================
# WITH MOCKS - Testing in Isolation
# ============================================================

print("\n2. WITH MOCKS")

print("""
from unittest.mock import Mock, patch

def get_user_data(user_id):
    response = requests.get(f"https://api.example.com/users/{user_id}")
    return response.json()

def test_get_user_data():
    # Mock the API response
    mock_response = Mock()
    mock_response.json.return_value = {"id": 123, "name": "Alice"}
    
    with patch('requests.get', return_value=mock_response):
        user = get_user_data(123)
        assert user["id"] == 123
        assert user["name"] == "Alice"

Benefits:
- Fast (no network call)
- Reliable (always works)
- No internet needed
- Free (no API usage)
- Can test errors easily
""")


# ============================================================
# BENEFITS SUMMARY
# ============================================================

print("\n" + "-" * 30)
print("BENEFITS OF MOCKING")
print("-" * 30)
print("""
- Test code in isolation
- Faster tests (no external calls)
- More reliable tests
- No external dependencies
- Can test error scenarios
- Control what the dependency returns
- Verify how your code uses dependencies
- Write tests before APIs are ready
""")

Benefits of mocking:

  • Isolation — test code without external dependencies
  • Speed — mocks are much faster than real calls
  • Reliability — tests always pass/fail consistently
  • Control — simulate any scenario (success, errors, edge cases)
  • Verification — check how your code interacts with dependencies

Quick Check: What is the main benefit of using mocks? (Answer: Testing code in isolation without external dependencies)

Mock Objects

2

Creating and Configuring Mocks

A Mock object is a flexible fake object that you can configure to behave however you need.

# Mock Objects

from unittest.mock import Mock

print("=" * 50)
print("MOCK OBJECTS")
print("=" * 50)

# ============================================================
# CREATING A BASIC MOCK
# ============================================================

print("\n1. CREATING A BASIC MOCK")

# Create a mock
mock = Mock()
print(f"   Mock: {mock}")

# Call methods on it - they work!
mock.some_method()
mock.another_method("arg1", key="value")
print("   Mock methods can be called without definition")


# ============================================================
# CONFIGURING RETURN VALUES
# ============================================================

print("\n2. CONFIGURING RETURN VALUES")

# Set return value
mock = Mock()
mock.get_data.return_value = {"id": 1, "name": "Alice"}

result = mock.get_data()
print(f"   get_data() -> {result}")

# Set return value with arguments
mock.calculate.return_value = 42
print(f"   calculate(1, 2) -> {mock.calculate(1, 2)}")


# ============================================================
# CONFIGURING SIDE EFFECTS
# ============================================================

print("\n3. CONFIGURING SIDE EFFECTS")

# Side effect as a function
def raise_error(*args, **kwargs):
    raise ValueError("Something went wrong")

mock = Mock()
mock.process.side_effect = raise_error

try:
    mock.process()
except ValueError as e:
    print(f"   Side effect raised: {e}")

# Side effect as a list (returns values in sequence)
mock = Mock()
mock.get_id.side_effect = [1, 2, 3, 4, 5]

print(f"   get_id() -> {mock.get_id()}")
print(f"   get_id() -> {mock.get_id()}")
print(f"   get_id() -> {mock.get_id()}")


# ============================================================
# MOCKING ATTRIBUTES
# ============================================================

print("\n4. MOCKING ATTRIBUTES")

# Mock object with attributes
mock = Mock()
mock.name = "Test"
mock.age = 30
mock.address.street = "123 Main St"
mock.address.city = "Boston"

print(f"   name: {mock.name}")
print(f"   age: {mock.age}")
print(f"   address.street: {mock.address.street}")


# ============================================================
# MOCK WITH SPEC
# ============================================================

print("\n5. MOCK WITH SPEC (restrict to specific attributes)")

class User:
    def __init__(self, name):
        self.name = name
    
    def greet(self):
        return f"Hello, {self.name}"

# Mock with spec - only allows methods that exist in User
mock_user = Mock(spec=User)
mock_user.name = "Alice"
mock_user.greet.return_value = "Hello, Alice"

print(f"   mock_user.name: {mock_user.name}")
print(f"   mock_user.greet(): {mock_user.greet()}")

# This would raise an AttributeError (not in spec)
# mock_user.non_existent()


# ============================================================
# MOCKING WITH RETURN VALUES
# ============================================================

print("\n6. MOCKING WITH RETURN VALUES")

# Complex return values
mock = Mock()
mock.get_user.return_value = {
    "id": 1,
    "name": "Alice",
    "email": "alice@example.com",
    "active": True
}

user = mock.get_user(1)
print(f"   User: {user}")

# Mock raising an exception
mock = Mock()
mock.fetch.side_effect = ConnectionError("Network error")

try:
    mock.fetch()
except ConnectionError as e:
    print(f"   Raised: {e}")

Mock objects key points:

  • Mock() — creates a flexible mock object
  • return_value — sets what the mock returns
  • side_effect — sets a function, exception, or sequence
  • spec — restricts mock to specific attributes
  • Attributes — can be set directly on the mock

Quick Check: How do you make a mock return a specific value? (Answer: Set mock.method.return_value = value)

Patching with patch

3

Replacing Real Objects with Mocks

patch is a decorator/context manager that replaces a real object with a mock during a test.

# Patching with patch

from unittest.mock import patch, Mock
import requests

print("=" * 50)
print("PATCHING WITH PATCH")
print("=" * 50)

# ============================================================
# CODE TO TEST
# ============================================================

def get_user_data(user_id):
    """Get user data from API"""
    response = requests.get(f"https://api.example.com/users/{user_id}")
    if response.status_code == 200:
        return response.json()
    return None

def save_user_data(user_id, data):
    """Save user data to API"""
    response = requests.post(f"https://api.example.com/users/{user_id}", json=data)
    return response.status_code == 201


# ============================================================
# PATCH AS DECORATOR
# ============================================================

print("\n1. PATCH AS DECORATOR")

print("""
import pytest
from unittest.mock import patch

@patch('requests.get')
def test_get_user_data(mock_get):
    # Configure the mock
    mock_response = Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {"id": 1, "name": "Alice"}
    mock_get.return_value = mock_response
    
    # Call the function
    result = get_user_data(1)
    
    # Assert
    assert result == {"id": 1, "name": "Alice"}
    mock_get.assert_called_once_with("https://api.example.com/users/1")
""")


# ============================================================
# PATCH AS CONTEXT MANAGER
# ============================================================

print("\n2. PATCH AS CONTEXT MANAGER")

print("""
def test_get_user_data():
    with patch('requests.get') as mock_get:
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"id": 1, "name": "Alice"}
        mock_get.return_value = mock_response
        
        result = get_user_data(1)
        assert result == {"id": 1, "name": "Alice"}
""")


# ============================================================
# PATCHING MULTIPLE THINGS
# ============================================================

print("\n3. PATCHING MULTIPLE THINGS")

print("""
@patch('requests.post')
@patch('requests.get')
def test_save_and_get_user(mock_get, mock_post):
    # Note: patches are applied bottom to top
    # mock_post is first parameter, mock_get is second
    
    # Configure get
    mock_response = Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {"id": 1, "name": "Alice"}
    mock_get.return_value = mock_response
    
    # Configure post
    mock_post_response = Mock()
    mock_post_response.status_code = 201
    mock_post.return_value = mock_post_response
    
    # Test save
    result = save_user_data(1, {"name": "Bob"})
    assert result is True
    
    # Test get
    user = get_user_data(1)
    assert user == {"id": 1, "name": "Alice"}
""")


# ============================================================
# PATCHING AN OBJECT'S ATTRIBUTE
# ============================================================

print("\n4. PATCHING AN OBJECT'S ATTRIBUTE")

print("""
# Mock a function from a module
from myapp import database

@patch('myapp.database.get_connection')
def test_database(mock_get_connection):
    mock_conn = Mock()
    mock_conn.execute.return_value = [{"id": 1}]
    mock_get_connection.return_value = mock_conn
    
    # Test code that uses database.get_connection()
    result = get_users()
    assert len(result) == 1
""")


# ============================================================
# PATCHING WITH SPEC
# ============================================================

print("\n5. PATCHING WITH SPEC")

print("""
@patch('requests.get')
def test_with_spec(mock_get):
    # Create mock with spec
    mock_get.return_value = Mock(spec=['status_code', 'json'])
    mock_get.return_value.status_code = 200
    mock_get.return_value.json.return_value = {"id": 1}
    
    result = get_user_data(1)
    assert result == {"id": 1}
""")

Patching key points:

  • @patch('module.function') — decorator that replaces a function
  • with patch('module.function') as mock — context manager
  • Multiple patches — use multiple decorators (bottom to top)
  • Restores automatically — patch restores the original after the test

Quick Check: What does patch do? (Answer: It replaces a real object with a mock during a test)

Mock Assertions

4

Verifying How Mocks Were Used

Mock objects record how they're used. You can assert that specific calls were made.

# Mock Assertions

from unittest.mock import Mock, call

print("=" * 50)
print("MOCK ASSERTIONS")
print("=" * 50)

# ============================================================
# CREATING A MOCK
# ============================================================

print("\n1. CREATING A MOCK")

mock = Mock()
mock.process("data1")
mock.process("data2", retry=True)
mock.cleanup()

print("   Mock called with different arguments")


# ============================================================
# ASSERT CALLED
# ============================================================

print("\n2. ASSERT CALLED")

# Check if mock was called
mock = Mock()
mock.do_something()

print(f"   mock.called: {mock.called}")
print(f"   mock.call_count: {mock.call_count}")

# Assert in tests
# mock.assert_called()  # Passes
# mock.assert_called_once()  # Passes


# ============================================================
# ASSERT CALLED WITH
# ============================================================

print("\n3. ASSERT CALLED WITH")

mock = Mock()
mock.save("user_data", format="json")

# Check the last call arguments
print(f"   mock.call_args: {mock.call_args}")
print(f"   mock.call_args[0]: {mock.call_args[0]}")  # positional args
print(f"   mock.call_args[1]: {mock.call_args[1]}")  # keyword args")

# In tests:
# mock.assert_called_with("user_data", format="json")
# mock.assert_called_once_with("user_data", format="json")


# ============================================================
# ASSERT CALLED WITH MULTIPLE CALLS
# ============================================================

print("\n4. ASSERT CALLED WITH MULTIPLE CALLS")

mock = Mock()
mock.log("Starting")
mock.log("Processing")
mock.log("Done")

# Check call count
print(f"   call_count: {mock.call_count}")

# Check all calls
print(f"   mock.call_args_list: {mock.call_args_list}")

# In tests:
# mock.assert_has_calls([
#     call("Starting"),
#     call("Processing"),
#     call("Done")
# ])


# ============================================================
# ASSERT NOT CALLED
# ============================================================

print("\n5. ASSERT NOT CALLED")

mock = Mock()
# mock.do_something()  # Commented out

print(f"   mock.called: {mock.called}")
# mock.assert_not_called()  # Passes if not called


# ============================================================
# ASSERT ANY CALL
# ============================================================

print("\n6. ASSERT ANY CALL")

from unittest.mock import ANY

mock = Mock()
mock.process(10, "data", timestamp=12345)

# ANY matches any value
print(f"   mock.process called with ANY: {mock.process.called}")

# In tests:
# mock.assert_called_with(ANY, "data", timestamp=ANY)


# ============================================================
# ASSERT WITH SPECIFIC ARGUMENTS
# ============================================================

print("\n7. ASSERT WITH SPECIFIC ARGUMENTS")

def test_mock_assertions():
    mock = Mock()
    mock.save_data("file.txt", overwrite=True, backup=False)
    
    # Check arguments
    args, kwargs = mock.call_args
    print(f"   args: {args}")
    print(f"   kwargs: {kwargs}")

test_mock_assertions()


# ============================================================
# ALL ASSERTION METHODS
# ============================================================

print("\n" + "-" * 30)
print("MOCK ASSERTION METHODS")
print("-" * 30)
print("""
┌─────────────────────────┬────────────────────────────────────────────┐
│ Method                  │ What it checks                            │
├─────────────────────────┼────────────────────────────────────────────┤
│ assert_called()         │ Mock was called at least once             │
│ assert_called_once()    │ Mock was called exactly once              │
│ assert_called_with()    │ Mock was called with specific args        │
│ assert_called_once_with()│ Mock was called exactly once with args   │
│ assert_any_call()       │ Mock was called with any of these args    │
│ assert_has_calls()      │ Mock had these calls (in order)           │
│ assert_not_called()     │ Mock was never called                     │
└─────────────────────────┴────────────────────────────────────────────┘
""")

Mock assertions key points:

  • assert_called() — was the mock called?
  • assert_called_with() — was it called with specific arguments?
  • assert_has_calls() — was it called with a sequence of calls?
  • assert_not_called() — was it never called?
  • ANY — matches any value in assertions

Quick Check: How do you check that a mock was called exactly once with specific arguments? (Answer: mock.assert_called_once_with(args))

Real-World Example

5

Testing an API Client

# Real-World Example: Testing an API Client

import json
from unittest.mock import patch, Mock
import requests

print("=" * 60)
print("TESTING AN API CLIENT")
print("=" * 60)

# ============================================================
# API CLIENT CODE
# ============================================================

class APIClient:
    """A simple API client that makes HTTP requests"""
    
    def __init__(self, base_url):
        self.base_url = base_url
        self.session = requests.Session()
        self.session.headers.update({"User-Agent": "APIClient/1.0"})
    
    def get_user(self, user_id):
        """Get a user by ID"""
        try:
            response = self.session.get(f"{self.base_url}/users/{user_id}")
            response.raise_for_status()
            return response.json()
        except requests.RequestException as e:
            return {"error": str(e)}
    
    def create_user(self, user_data):
        """Create a new user"""
        try:
            response = self.session.post(f"{self.base_url}/users", json=user_data)
            response.raise_for_status()
            return response.json()
        except requests.RequestException as e:
            return {"error": str(e)}
    
    def delete_user(self, user_id):
        """Delete a user"""
        try:
            response = self.session.delete(f"{self.base_url}/users/{user_id}")
            response.raise_for_status()
            return {"success": True}
        except requests.RequestException as e:
            return {"error": str(e)}


# ============================================================
# TESTS WITH MOCKS
# ============================================================

print("\n1. TEST GET USER")

def test_get_user_success():
    """Test successful user retrieval"""
    print("   Testing get_user success...")
    
    # Mock the session
    mock_session = Mock()
    mock_response = Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {"id": 1, "name": "Alice", "email": "alice@example.com"}
    mock_session.get.return_value = mock_response
    
    # Create client with mocked session
    client = APIClient("https://api.example.com")
    client.session = mock_session
    
    # Test
    result = client.get_user(1)
    print(f"   Result: {result}")
    
    # Verify
    mock_session.get.assert_called_with("https://api.example.com/users/1")

def test_get_user_not_found():
    """Test user not found"""
    print("\n   Testing get_user not found...")
    
    mock_session = Mock()
    mock_response = Mock()
    mock_response.raise_for_status.side_effect = requests.HTTPError("404 Not Found")
    mock_session.get.return_value = mock_response
    
    client = APIClient("https://api.example.com")
    client.session = mock_session
    
    result = client.get_user(999)
    print(f"   Result: {result}")
    
    assert "error" in result

def test_create_user():
    """Test creating a user"""
    print("\n   Testing create_user...")
    
    mock_session = Mock()
    mock_response = Mock()
    mock_response.status_code = 201
    mock_response.json.return_value = {"id": 2, "name": "Bob", "email": "bob@example.com"}
    mock_session.post.return_value = mock_response
    
    client = APIClient("https://api.example.com")
    client.session = mock_session
    
    user_data = {"name": "Bob", "email": "bob@example.com"}
    result = client.create_user(user_data)
    print(f"   Result: {result}")
    
    mock_session.post.assert_called_with(
        "https://api.example.com/users",
        json=user_data
    )

def test_delete_user():
    """Test deleting a user"""
    print("\n   Testing delete_user...")
    
    mock_session = Mock()
    mock_response = Mock()
    mock_response.status_code = 204
    mock_session.delete.return_value = mock_response
    
    client = APIClient("https://api.example.com")
    client.session = mock_session
    
    result = client.delete_user(1)
    print(f"   Result: {result}")
    
    mock_session.delete.assert_called_with("https://api.example.com/users/1")

# ============================================================
# RUNNING TESTS
# ============================================================

print("\n2. RUNNING TESTS")
test_get_user_success()
test_get_user_not_found()
test_create_user()
test_delete_user()

print("\n" + "=" * 60)
print("KEY TAKEAWAYS:")
print("=" * 60)
print("""
- Mock external dependencies like API calls
- Test both success and error cases
- Verify that correct API endpoints are called
- Test with different scenarios (404, 201, 204)
- Mocks make tests fast and reliable
""")

Real-world example key points:

  • API client — code that makes HTTP requests
  • Mock session — replace real requests with mocks
  • Test success — mock successful responses
  • Test errors — mock errors like 404
  • Verify calls — check that correct endpoints were called

Quick Check: Why would you mock an API call in a test? (Answer: To make tests fast, reliable, and independent of external services)

Best Practices

6

Using Mocks Effectively

# Best Practices for Mocking

print("=" * 60)
print("BEST PRACTICES FOR MOCKING")
print("=" * 60)

# ============================================================
# 1. MOCK EXTERNAL DEPENDENCIES ONLY
# ============================================================

print("\n1. MOCK EXTERNAL DEPENDENCIES ONLY")

print("""
# Good - mock external API calls
@patch('requests.get')
def test_api_call(mock_get):
    ...

# Bad - mock internal functions (overkill)
@patch('myapp.helper.calculate')
def test_calculation(mock_calc):
    # Don't mock internal logic
    ...
""")


# ============================================================
# 2. SPECIFY RETURN VALUES CLEARLY
# ============================================================

print("\n2. SPECIFY RETURN VALUES CLEARLY")

print("""
# Good - clear and specific
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"id": 1, "name": "Alice"}

# Bad - vague
mock_response = Mock()
mock_response.json.return_value = {"id": 1}  # Missing expected fields
""")


# ============================================================
# 3. VERIFY MOCK CALLS
# ============================================================

print("\n3. VERIFY MOCK CALLS")

print("""
# Good - verify the call
def test_get_user():
    mock_get = Mock()
    get_user(mock_get, 1)
    mock_get.assert_called_with("https://api.example.com/users/1")

# Bad - no verification
def test_get_user():
    mock_get = Mock()
    get_user(mock_get, 1)
    # No assertions about how it was called
""")


# ============================================================
# 4. USE SPEC FOR REALISTIC MOCKS
# ============================================================

print("\n4. USE SPEC FOR REALISTIC MOCKS")

print("""
# Good - use spec
mock_response = Mock(spec=['status_code', 'json', 'headers'])
mock_response.status_code = 200

# Bad - no spec (allows any attribute)
mock_response = Mock()
mock_response.invalid_attribute = "value"  # Shouldn't work
""")


# ============================================================
# 5. CLEAN UP MOCKS (automatic with patch)
# ============================================================

print("\n5. CLEAN UP MOCKS (automatic with patch)")

print("""
# Good - patch handles cleanup automatically
@patch('requests.get')
def test_api(mock_get):
    ...

# Bad - manual cleanup (error-prone)
def test_api():
    mock_get = requests.get
    requests.get = Mock()
    try:
        # test
        pass
    finally:
        requests.get = mock_get
""")


# ============================================================
# 6. DON'T MOCK WHAT YOU DON'T OWN
# ============================================================

print("\n6. DON'T MOCK WHAT YOU DON'T OWN")

print("""
# Good - mock your code's dependencies
@patch('myapp.external_api.call')
def test_my_code(mock_api):
    ...

# Bad - mock built-in Python functions
@patch('open')  # Don't mock built-ins
def test_file(mock_open):
    ...
""")


# ============================================================
# 7. SUMMARY
# ============================================================

print("\n" + "=" * 60)
print("BEST PRACTICES SUMMARY")
print("=" * 60)
print("""
- Mock external dependencies only
- Be specific with return values
- Verify mock calls
- Use spec for realistic mocks
- Clean up mocks (patch does this)
- Don't mock what you don't own
- Test both success and error cases
- Keep mocks simple
""")

Best practices summary:

  • Mock external dependencies — not internal logic
  • Be specific — set clear return values
  • Verify calls — check how mocks were used
  • Use spec — make mocks realistic
  • Don't mock built-ins — mock your own dependencies

Quick Check: What should you not mock? (Answer: Built-in Python functions and internal logic)

Try It Yourself

Experiment with mocking in the editor below.

Loading Pyodide... 0%
Python Code Editor
==================================================
MOCKING - PRACTICE
==================================================

1. BASIC MOCK
mock.get_data() = {'id': 1, 'name': 'Test'}
side_effect: 1, 2, 3

2. MOCK WITH PATCH
Mocked result: {'id': 999, 'name': 'Mocked'}
Original: {'id': 1, 'name': 'Alice'}

3. MOCK ASSERTIONS
Called: True
Call count: 2
Last call: call('data2', retry=True)
🏆

You've Got It!

You now understand mocking in Python. You know how to create mock objects, use patch, and verify how mocks were used in your tests.

Quick Quiz

Test what you've learned:

1. What is mocking in testing?
2. How do you make a mock return a specific value?
3. What does patch do?
4. Which method checks if a mock was called with specific arguments?
5. Why should you use mocks in tests?

Frequently Asked Questions

What is mocking in Python? ▼

Mocking is the practice of replacing real objects with fake ones during testing. It allows you to test code in isolation without relying on external dependencies like databases, APIs, or network connections.

What's the difference between Mock and patch? ▼

Mock is a class that creates mock objects. patch is a function/decorator that temporarily replaces a real object with a mock. You use Mock to create fake objects and patch to replace real ones with mocks.

How do I mock an API call? ▼

Use patch to replace the function that makes the API call. For example, @patch('requests.get') replaces requests.get with a mock. Then configure the mock to return a fake response.

What is the spec parameter in Mock? ▼

The spec parameter restricts the mock to only have the attributes and methods of the specified class or object. This helps make mocks more realistic and catches errors when you try to call methods that don't exist.

When should I use mocking? ▼

Use mocking when testing code that depends on external services (APIs, databases, files), slow operations, or when you need to test error scenarios that are hard to reproduce with real dependencies.

Can I use mocking with pytest? ▼

Yes! pytest works perfectly with unittest.mock. You can use patch as a decorator or context manager in pytest tests. Many developers use pytest with unittest.mock for testing.

Where to Go From Here

Now that you understand mocking, check out these related topics:

Unit Testing

Learn more about unit testing with unittest.

Learn More →

Pytest Framework

Learn about pytest and how it works with mocks.

Learn More →

Testing Assignments

Practice your testing skills with assignments.

Learn More →
Interview Resources
  • Python Syntax & Variables Interview Questions
  • Top SQL Interview Questions & Answers
  • SQL Joins: Displaying Data from Multiple Tables FAQ
  • Python Lists and Dictionaries Interview Questions
  • Python OOP Interview Questions
  • SQL Set Operators Interview Questions
Previous: Pytest Next: Testing Assignments →