CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pytest-mock

Thin-wrapper around the mock package for easier use with pytest

Pending
Overview
Eval results
Files

fixtures.mddocs/

Pytest Fixtures

pytest-mock provides MockerFixture instances through pytest fixtures at multiple scopes, enabling flexible mock management across different test organization patterns. Each fixture provides the same MockerFixture interface but with different lifetimes and cleanup behaviors.

Capabilities

Function-Scoped Fixture

The default mocker fixture provides a MockerFixture instance that's created fresh for each test function and automatically cleaned up afterwards.

@pytest.fixture
def mocker(pytestconfig) -> Generator[MockerFixture, None, None]:
    """
    Function-scoped mocker fixture (default scope).
    
    Creates a new MockerFixture instance for each test function.
    All mocks are automatically stopped after the test completes.
    
    Yields:
    MockerFixture instance for the test function
    """

Usage examples:

def test_function_scope_basic(mocker):
    # Fresh mocker instance for this test
    mock_func = mocker.patch('os.path.exists')
    mock_func.return_value = True
    
    result = check_file_exists('test.txt')
    assert result is True
    mock_func.assert_called_once_with('test.txt')
    # Mock automatically cleaned up after test

def test_function_scope_independent(mocker):
    # Completely independent mocker instance
    mock_func = mocker.patch('os.path.exists') 
    mock_func.return_value = False
    
    result = check_file_exists('other.txt')
    assert result is False
    # No interference from previous test

class TestWithFunctionScope:
    def test_method_one(self, mocker):
        # Fresh mocker for this method
        spy = mocker.spy(Calculator, 'add')
        calc = Calculator()
        result = calc.add(2, 3)
        spy.assert_called_once_with(2, 3)
    
    def test_method_two(self, mocker):
        # Independent mocker instance
        mock_calc = mocker.create_autospec(Calculator)
        mock_calc.add.return_value = 100
        assert mock_calc.add(1, 1) == 100

Class-Scoped Fixture

The class_mocker fixture provides a MockerFixture instance shared across all test methods in a test class, with cleanup after the class completes.

@pytest.fixture(scope="class")
def class_mocker(pytestconfig) -> Generator[MockerFixture, None, None]:
    """
    Class-scoped mocker fixture.
    
    Creates one MockerFixture instance shared by all test methods
    in a test class. Mocks persist across methods and are cleaned
    up after the class completes.
    
    Yields:
    MockerFixture instance for the test class
    """

Usage examples:

class TestDatabaseOperations:
    def test_connection_setup(self, class_mocker):
        # Mock persists for entire class
        self.db_mock = class_mocker.patch('myapp.database.connect')
        self.db_mock.return_value = 'mock_connection'
        
        setup_database()
        self.db_mock.assert_called_once()
    
    def test_query_execution(self, class_mocker):
        # Same mocker instance, mocks still active
        query_mock = class_mocker.patch('myapp.database.execute_query')
        query_mock.return_value = [{'id': 1, 'name': 'test'}]
        
        results = run_query('SELECT * FROM users')
        
        # db_mock from previous test is still active
        assert self.db_mock.called
        query_mock.assert_called_once()
        assert len(results) == 1
    
    def test_connection_cleanup(self, class_mocker):
        # All mocks from class still available
        cleanup_mock = class_mocker.patch('myapp.database.disconnect')
        
        cleanup_database()
        
        # Can verify interactions across the whole class
        assert self.db_mock.call_count >= 1
        cleanup_mock.assert_called_once()
    
    # All mocks automatically cleaned up after class completes

# Different class gets fresh mocker instance
class TestFileOperations:
    def test_independent_mocking(self, class_mocker):
        # Completely separate from TestDatabaseOperations
        file_mock = class_mocker.patch('builtins.open')
        file_mock.return_value.__enter__.return_value.read.return_value = 'content'
        
        data = read_file('test.txt')
        assert data == 'content'

Module-Scoped Fixture

The module_mocker fixture provides a MockerFixture instance shared across all tests in a module, with cleanup after the module completes.

@pytest.fixture(scope="module")
def module_mocker(pytestconfig) -> Generator[MockerFixture, None, None]:
    """
    Module-scoped mocker fixture.
    
    Creates one MockerFixture instance shared by all tests
    in a module. Mocks persist across functions and classes
    and are cleaned up after the module completes.
    
    Yields:
    MockerFixture instance for the test module
    """

Usage examples:

# test_api_integration.py

# Module-level setup with module_mocker
@pytest.fixture(scope="module", autouse=True)
def setup_api_mocks(module_mocker):
    """Setup common mocks for entire module."""
    # Mock external API calls for all tests in module
    module_mocker.patch('requests.get', side_effect=mock_api_response)
    module_mocker.patch('requests.post', side_effect=mock_api_post)
    
    # Mock authentication for all tests
    auth_mock = module_mocker.patch('myapp.auth.verify_token')
    auth_mock.return_value = True
    
    return module_mocker

def test_get_user_data(module_mocker):
    # Uses module-level mocks automatically
    user_data = fetch_user_data(user_id=123)
    
    # Can add test-specific mocks too
    cache_mock = module_mocker.patch('myapp.cache.get')
    cached_data = get_cached_user(user_id=123)
    
    assert user_data['id'] == 123
    cache_mock.assert_called_once()

def test_create_user(module_mocker):
    # Same module-level mocks still active
    new_user = create_user({'name': 'Test User'})
    
    # requests.post mock from setup is used
    assert new_user['status'] == 'created'

class TestUserManagement:
    def test_update_user(self, module_mocker):
        # Module-level mocks available in classes too
        result = update_user(123, {'name': 'Updated'})
        assert result['success'] is True
    
    def test_delete_user(self, module_mocker):
        # All module mocks persist across class methods
        result = delete_user(123)
        assert result['deleted'] is True

# All module-level mocks cleaned up when module completes

Package-Scoped Fixture

The package_mocker fixture provides a MockerFixture instance shared across all tests in a Python package, with cleanup after the package completes.

@pytest.fixture(scope="package")
def package_mocker(pytestconfig) -> Generator[MockerFixture, None, None]:
    """
    Package-scoped mocker fixture.
    
    Creates one MockerFixture instance shared by all tests
    in a package. Mocks persist across modules and are
    cleaned up after the package completes.
    
    Yields:
    MockerFixture instance for the test package
    """

Usage examples:

# tests/conftest.py - Package-level configuration
@pytest.fixture(scope="package", autouse=True)
def package_setup(package_mocker):
    """Setup package-wide mocks and configuration."""
    # Mock external services for entire test package
    package_mocker.patch('myapp.external.payment_service.charge')
    package_mocker.patch('myapp.external.email_service.send')
    
    # Mock environment configuration
    package_mocker.patch.dict('os.environ', {
        'DATABASE_URL': 'sqlite:///:memory:',
        'REDIS_URL': 'redis://localhost:6379/15',
        'API_KEY': 'test-api-key'
    })
    
    return package_mocker

# tests/test_payments.py
def test_process_payment(package_mocker):
    # Uses package-level payment service mock
    result = process_payment(amount=100, card_token='tok_123')
    assert result['status'] == 'success'

# tests/test_notifications.py  
def test_send_notification(package_mocker):
    # Uses package-level email service mock
    send_welcome_email('user@example.com')
    # Email mock is already configured at package level

# tests/integration/test_workflows.py
def test_complete_workflow(package_mocker):
    # All package-level mocks available in subdirectories
    workflow = CompleteUserWorkflow()
    result = workflow.execute(user_data={'email': 'test@example.com'})
    
    # Payment and email mocks from package level are used
    assert result['payment_processed'] is True
    assert result['welcome_sent'] is True

Session-Scoped Fixture

The session_mocker fixture provides a MockerFixture instance shared across the entire test session, with cleanup after all tests complete.

@pytest.fixture(scope="session")
def session_mocker(pytestconfig) -> Generator[MockerFixture, None, None]:
    """
    Session-scoped mocker fixture.
    
    Creates one MockerFixture instance shared by all tests
    in the session. Mocks persist across all packages and
    modules and are cleaned up after the session completes.
    
    Yields:
    MockerFixture instance for the test session
    """

Usage examples:

# conftest.py at root of test suite
@pytest.fixture(scope="session", autouse=True)
def global_test_setup(session_mocker):
    """Setup session-wide mocks for external dependencies."""
    # Mock third-party services for entire test session
    session_mocker.patch('stripe.api_resources.Charge.create')
    session_mocker.patch('boto3.client')  # Mock AWS services
    session_mocker.patch('redis.Redis.from_url')
    
    # Mock time-based functions for consistent testing
    import datetime
    fixed_time = datetime.datetime(2023, 1, 1, 12, 0, 0)
    session_mocker.patch('datetime.datetime.now', return_value=fixed_time)
    
    return session_mocker

# Any test file in the session
def test_stripe_integration(session_mocker):
    # Uses session-level stripe mock automatically
    charge = create_payment_charge(amount=1000)
    assert charge['status'] == 'succeeded'

def test_aws_s3_upload(session_mocker):
    # Uses session-level boto3 mock automatically  
    result = upload_file_to_s3('test.txt', 'content')
    assert result['success'] is True

# Different package, same session mocks
def test_redis_caching(session_mocker):
    # Uses session-level redis mock automatically
    cache_key = 'test:key'
    set_cache(cache_key, 'value')
    value = get_cache(cache_key)
    assert value == 'value'

def test_time_dependent_logic(session_mocker):
    # Uses session-level datetime mock - consistent time across all tests
    timestamp = get_current_timestamp()
    assert timestamp == '2023-01-01T12:00:00'

Fixture Scope Comparison

# Scope lifecycles and use cases

def test_scope_demonstration():
    """
    Fixture Scopes (from shortest to longest lived):
    
    1. function (default) - New MockerFixture per test function
       - Use for: Isolated test mocks, no shared state needed
       - Cleanup: After each test function
    
    2. class - One MockerFixture per test class  
       - Use for: Shared setup within related test methods
       - Cleanup: After test class completes
    
    3. module - One MockerFixture per test module
       - Use for: Expensive mocks, module-wide configuration
       - Cleanup: After test module completes
    
    4. package - One MockerFixture per test package
       - Use for: Package-wide external service mocks
       - Cleanup: After test package completes
    
    5. session - One MockerFixture for entire test session
       - Use for: Global mocks, third-party service mocks
       - Cleanup: After all tests complete
    """

# Choosing the right scope:
def test_choosing_fixture_scope():
    """
    Guidelines for fixture scope selection:
    
    - function: Default choice, ensures test isolation
    - class: When test methods need shared mock state
    - module: For expensive-to-create mocks used across module
    - package: For mocking external dependencies across packages
    - session: For global mocks that should never change
    
    Trade-offs:
    - Longer scopes = better performance, less isolation
    - Shorter scopes = better isolation, more setup overhead
    """

Install with Tessl CLI

npx tessl i tessl/pypi-pytest-mock

docs

core-mocking.md

fixtures.md

index.md

mock-creation.md

tile.json