Skip to main content

Type-safe mocking for pytest - preserve original type hints while using mock features

Project description

typed-pytest

Type-safe mocking for pytest - preserve original type hints while using mock features.

Problem

When using pytest with unittest.mock, type inference becomes problematic:

from unittest.mock import MagicMock
from typing import cast

class UserService:
    def get_user(self, user_id: int) -> dict:
        return {"id": user_id, "name": "John"}

# Problem 1: MagicMock loses original type hints
mock_service = MagicMock(spec=UserService)
mock_service.get_user(1)  # No autocomplete for get_user parameters

# Problem 2: cast() loses mock method hints
service = cast(UserService, mock_service)
service.get_user.assert_called_once()  # assert_called_once has no type hint!

Solution

typed-pytest provides type-safe mocking with:

  • Type-checked mock assertions - assert_called_once_with() and other assertions have full type hints
  • Type-checked mock properties - return_value, side_effect, call_count are properly typed
  • IDE auto-completion for mock methods
from typed_pytest import TypedMock, typed_mock

mock_service: TypedMock[UserService] = typed_mock(UserService)

# Mock methods have type hints
mock_service.get_user.assert_called_once_with(1)  # Type-checked!
mock_service.get_user.return_value = {"id": 1}    # Type-checked!

Installation

# Using uv
uv add typed-pytest

# Using pip
pip install typed-pytest

Requirements

  • Python 3.13+
  • pytest 8.0+
  • pytest-mock 3.11+

Quick Start

from typed_pytest import typed_mock, TypedMock, TypedMocker

# Method 1: Direct mock creation
def test_user_service():
    mock_service: TypedMock[UserService] = typed_mock(UserService)
    mock_service.get_user.return_value = {"id": 1, "name": "Test"}

    result = mock_service.get_user(1)

    assert result == {"id": 1, "name": "Test"}
    mock_service.get_user.assert_called_once_with(1)

# Method 2: Using pytest fixture
def test_with_fixture(typed_mocker: TypedMocker):
    mock_service = typed_mocker.mock(UserService)
    mock_service.get_user.return_value = {"id": 1}

    mock_service.get_user(1)
    mock_service.get_user.assert_called_once_with(1)

Stub Generator

typed-pytest-generator generates stub files for IDE auto-completion support. This allows your IDE to provide method signatures and type hints when using typed_mock().

Basic Usage

# Generate stubs for your classes
typed-pytest-generator -t myapp.services.UserService myapp.repos.ProductRepository

# Specify custom output directory
typed-pytest-generator -t myapp.services.UserService -o my_stubs

# Include private methods (starting with _)
typed-pytest-generator -t myapp.services.UserService --include-private

# Verbose output
typed-pytest-generator -t myapp.services.UserService -v

Generated Files

The generator creates the following structure:

typed_pytest_stubs/
├── __init__.py      # Re-exports all stub classes
└── _runtime.py      # Runtime class definitions with method signatures

Using Generated Stubs

# Import typed_mock from stubs package for full auto-completion
from typed_pytest_stubs import typed_mock, UserService

def test_user_service():
    # typed_mock returns UserService_TypedMock with full IDE support
    mock = typed_mock(UserService)

    mock.get_user              # ✅ Auto-complete for method names
    mock.get_user.return_value # ✅ Auto-complete for mock properties
    mock.get_user.assert_called_once_with(1)  # ✅ Type-checked!

    mock.get_user.return_value = {"id": 1, "name": "Test"}
    result = mock.get_user(1)

Configuration

pyproject.toml Configuration

You can configure typed-pytest-generator in your pyproject.toml file. This allows you to define targets, output directory, and other options without passing them via CLI every time.

[tool.typed-pytest-generator]
# List of fully qualified class names to generate stubs for
# Supports wildcard patterns: "module.*" matches all classes in the module
targets = [
    "myapp.services.*",                      # All classes in myapp.services
    "myapp.repositories.ProductRepository",  # Specific class
]

# Output directory for generated stubs (default: "typed_pytest_stubs")
output-dir = "typed_pytest_stubs"

# Include private methods starting with _ (default: false)
include-private = false

# Exclude specific classes from stub generation
exclude-targets = [
    "myapp.internal.PrivateHelper",
    "myapp.legacy.DeprecatedService",
]

Once configured, simply run:

# Uses configuration from pyproject.toml (auto-discovered)
typed-pytest-generator

# With verbose output
typed-pytest-generator -v

CLI Options

CLI arguments take precedence over pyproject.toml configuration:

# Override targets from config
typed-pytest-generator -t myapp.services.NewService

# Override output directory
typed-pytest-generator -o custom_stubs

# Add exclusions (merged with config exclusions)
typed-pytest-generator -e myapp.services.SkipThis

# Use a specific config file
typed-pytest-generator -c /path/to/pyproject.toml

# Use wildcard to match all classes in a module
typed-pytest-generator -t "myapp.services.*"

# Combine options
typed-pytest-generator -t "myapp.services.*" -e myapp.services.Internal -o stubs -v

IDE and Type Checker Setup

Add typed_pytest_stubs/ to your .gitignore since these files are generated:

# Generated stub files
typed_pytest_stubs/

For pyright/pylance users, add the stubs directory to your pyproject.toml:

[tool.pyright]
include = ["src", "tests", "typed_pytest_stubs"]

Development

# Clone repository
git clone https://github.com/your-org/typed-pytest.git
cd typed-pytest

# Install dependencies
uv sync --all-extras

# Run tests
uv run pytest

# Run tests with coverage
uv run pytest --cov=src/typed_pytest --cov-report=term-missing

# Type check
uv run mypy src/
uv run pyright src/

# Lint
uv run ruff check src/ tests/
uv run ruff format src/ tests/

Documentation

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

typed_pytest-0.1.0.tar.gz (65.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

typed_pytest-0.1.0-py3-none-any.whl (31.8 kB view details)

Uploaded Python 3

File details

Details for the file typed_pytest-0.1.0.tar.gz.

File metadata

  • Download URL: typed_pytest-0.1.0.tar.gz
  • Upload date:
  • Size: 65.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for typed_pytest-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b8b8f33a9661dd6b5beee9f7ba732751ee347fbc21a87aca4461e2f634d54ccd
MD5 5036f54e4b3176378dc4e53a75710122
BLAKE2b-256 c16bade6432627fb1ef9cc59a667f7df577d4b5104aff1f80bac037584bb913c

See more details on using hashes here.

File details

Details for the file typed_pytest-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: typed_pytest-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 31.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for typed_pytest-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8a42bb92befcc9642af34e202794a5ef577e2c34165cb8fb263455de345f76a3
MD5 d580ca94c107cb9c65479b48f93b7331
BLAKE2b-256 b7d5390bea41506928ed1c2710b2ed0adf788f1fdcea33e2db6bab1ddf9d1c6f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page