Strategy Generation

The generation module creates Hypothesis strategies for generating test data. It provides type-to-strategy mapping, path parameter generation, request body generation, and header generation.

Overview

pytest-routes uses Hypothesis for property-based testing. The generation module bridges your application’s type system with Hypothesis strategies:

  1. Type Strategies - Map Python types to Hypothesis strategies

  2. Path Parameters - Generate valid path parameter values

  3. Request Bodies - Generate request payloads from models

  4. Headers - Generate HTTP headers for authentication, content-type, etc.

Quick Start

Get a strategy for any type:

from pytest_routes import strategy_for_type
from hypothesis import given

# Built-in types work automatically
int_strategy = strategy_for_type(int)
str_strategy = strategy_for_type(str)

# Use in Hypothesis tests
@given(value=strategy_for_type(int))
def test_with_integers(value: int):
    assert isinstance(value, int)

Register custom types:

from hypothesis import strategies as st
from pytest_routes import register_strategy

class UserId:
    def __init__(self, value: int):
        self.value = value

register_strategy(
    UserId,
    st.builds(UserId, value=st.integers(min_value=1, max_value=1000000))
)

Built-in Type Strategies

The following types have pre-registered strategies:

Type

Strategy

str

st.text(min_size=1, max_size=100)

int

st.integers(min_value=-1000, max_value=1000)

float

st.floats(allow_nan=False, allow_infinity=False)

bool

st.booleans()

uuid.UUID

st.uuids()

datetime

st.datetimes()

date

st.dates()

bytes

st.binary(min_size=1, max_size=100)

Generic types are also supported:

from pytest_routes import strategy_for_type

# Optional[str] -> st.none() | st.text()
strategy_for_type(Optional[str])

# list[int] -> st.lists(st.integers())
strategy_for_type(list[int])

# dict[str, int] -> st.dictionaries(st.text(), st.integers())
strategy_for_type(dict[str, int])

API Reference

Type Strategies

Strategy Registration Functions

pytest_routes.generation.strategies.register_strategy(typ, strategy, *, override=False)[source]

Register a custom strategy for a type.

Parameters:
  • typ (type) – The Python type.

  • strategy (SearchStrategy[Any]) – The Hypothesis strategy to use for this type.

  • override (bool) – If True, allow overriding an existing strategy. If False (default), raise ValueError if a strategy is already registered for this type.

Raises:

ValueError – If a strategy is already registered and override is False.

Return type:

None

Examples

>>> from hypothesis import strategies as st
>>> register_strategy(MyType, st.builds(MyType))
>>> register_strategy(MyType, st.builds(MyType, arg="new"), override=True)

Register a permanent strategy for a type.

Example

from hypothesis import strategies as st
from pytest_routes import register_strategy

# Register for a custom type
register_strategy(
    MyType,
    st.builds(MyType, name=st.text(min_size=1))
)

# Override an existing strategy
register_strategy(
    str,
    st.text(min_size=5, max_size=20),
    override=True
)
pytest_routes.generation.strategies.unregister_strategy(typ)[source]

Unregister a custom strategy.

Parameters:

typ (type) – The Python type to unregister.

Return type:

bool

Returns:

True if the type was previously registered, False otherwise.

Examples

>>> from hypothesis import strategies as st
>>> register_strategy(MyType, st.builds(MyType))
>>> unregister_strategy(MyType)
True
>>> unregister_strategy(MyType)
False
pytest_routes.generation.strategies.register_strategies(mapping, *, override=False)[source]

Register multiple strategies at once.

Parameters:
  • mapping (dict[type, SearchStrategy[Any]]) – Dictionary mapping types to their strategies.

  • override (bool) – If True, allow overriding existing strategies. If False (default), raise ValueError if any type already has a registered strategy.

Raises:

ValueError – If any type is already registered and override is False.

Return type:

None

Examples

>>> from hypothesis import strategies as st
>>> register_strategies(
...     {
...         MyType1: st.builds(MyType1),
...         MyType2: st.builds(MyType2),
...     }
... )

Register multiple strategies at once.

Example

from hypothesis import strategies as st
from pytest_routes import register_strategies

register_strategies({
    UserId: st.builds(UserId, st.integers(min_value=1)),
    Email: st.emails(),
    PhoneNumber: st.from_regex(r"\+\d{10,15}"),
})
pytest_routes.generation.strategies.get_registered_types()[source]

Get list of types with registered strategies.

Return type:

list[type]

Returns:

List of types that have custom strategies registered.

Examples

>>> types = get_registered_types()
>>> str in types
True

Strategy Helpers

pytest_routes.generation.strategies.strategy_for_type(typ)[source]

Get a Hypothesis strategy for a Python type.

Parameters:

typ (type) – The Python type to generate values for.

Return type:

SearchStrategy[Any]

Returns:

A Hypothesis SearchStrategy for the type.

The main function for getting a strategy for any type.

Type Resolution

The function resolves types in this order:

  1. Direct lookup in the registry

  2. Optional[X] unwrapping

  3. list[X] and dict[K, V] handling

  4. Dataclass/Pydantic model detection

  5. Hypothesis from_type() fallback

  6. Text fallback for unknown types

pytest_routes.generation.strategies.strategy_provider(typ)[source]

Decorator to register a strategy provider function.

Parameters:

typ (type) – The Python type to register a strategy for.

Return type:

Callable[[Callable[[], SearchStrategy[Any]]], Callable[[], SearchStrategy[Any]]]

Returns:

Decorator function that registers the strategy when applied.

Examples

>>> from hypothesis import strategies as st
>>> @strategy_provider(MyType)
... def my_custom_strategy():
...     return st.builds(MyType, field=st.text())

Decorator for registering strategy provider functions.

Example

from pytest_routes import strategy_provider
from hypothesis import strategies as st

@strategy_provider(MyComplexType)
def my_complex_strategy():
    return st.builds(
        MyComplexType,
        field1=st.text(),
        field2=st.integers(),
    )
pytest_routes.generation.strategies.temporary_strategy(typ, strategy)[source]

Temporarily register a strategy for the duration of a context.

The original strategy (if any) is restored when the context exits.

Parameters:
  • typ (type) – The Python type.

  • strategy (SearchStrategy[Any]) – The temporary Hypothesis strategy to use.

Yields:

None

Return type:

Generator[None, None, None]

Examples

>>> from hypothesis import strategies as st
>>> with temporary_strategy(str, st.just("test")):
...     # str strategy is now st.just("test")
...     result = strategy_for_type(str).example()
>>> # Original str strategy is restored

Context manager for temporary strategy registration.

Example

from pytest_routes import temporary_strategy, strategy_for_type
from hypothesis import strategies as st

# Normal str strategy
normal = strategy_for_type(str).example()

with temporary_strategy(str, st.just("fixed")):
    # Inside context, str always generates "fixed"
    assert strategy_for_type(str).example() == "fixed"

# Original strategy restored

Path Parameters

Path parameter generation.

pytest_routes.generation.path.generate_path_params(path_params, path)[source]

Generate valid path parameter combinations.

Parameters:
  • path_params (dict[str, type]) – Mapping of parameter names to their types.

  • path (str) – The route path pattern (reserved for future context-aware generation).

Return type:

SearchStrategy[dict[str, Any]]

Returns:

A Hypothesis strategy that generates dictionaries of path parameters.

pytest_routes.generation.path.format_path(path, params)[source]

Format a path pattern with parameter values.

Parameters:
  • path (str) – The path pattern with {param} placeholders.

  • params (dict[str, Any]) – Dictionary of parameter values.

Return type:

str

Returns:

The formatted path with parameters substituted.

Path parameter generation handles URL path variables:

from pytest_routes.generation.path import generate_path_params, format_path

# Generate values for path parameters
path_params = {"user_id": int, "post_id": int}
strategy = generate_path_params(path_params, "/users/{user_id}/posts/{post_id}")

# Format a path with generated values
values = {"user_id": 42, "post_id": 123}
url = format_path("/users/{user_id}/posts/{post_id}", values)
# Result: "/users/42/posts/123"

Request Bodies

Request body generation.

pytest_routes.generation.body.generate_body(body_type)[source]

Generate request body based on type annotation.

Parameters:

body_type (type | None) – The expected request body type, or None if no body.

Return type:

SearchStrategy[Any | None]

Returns:

A Hypothesis strategy that generates valid request bodies (as dicts).

Request body generation creates payloads for POST/PUT/PATCH requests:

from pytest_routes.generation.body import generate_body
from pydantic import BaseModel

class CreateUserRequest(BaseModel):
    name: str
    email: str
    age: int

# Generate valid request bodies
body_strategy = generate_body(CreateUserRequest)
example = body_strategy.example()
# Result: {"name": "...", "email": "...", "age": ...}

Header Generation

Header generation strategies for HTTP requests.

pytest_routes.generation.headers.register_header_strategy(header_name, strategy)[source]

Register a custom strategy for a specific HTTP header.

This allows users to override default header generation or add strategies for custom headers.

Parameters:
  • header_name (str) – The HTTP header name (case-insensitive).

  • strategy (SearchStrategy[str]) – A Hypothesis strategy that generates string values.

Return type:

None

Example

>>> from hypothesis import strategies as st
>>> register_header_strategy("X-Custom-ID", st.uuids().map(str))
pytest_routes.generation.headers.generate_headers(header_specs=None, *, include_content_type=False, include_accept=False, include_authorization=False)[source]

Generate HTTP headers based on specifications.

All header values are generated as strings to comply with HTTP standards.

Parameters:
  • header_specs (dict[str, type] | None) – Mapping of header names to their types. If None, only optional headers based on flags will be included.

  • include_content_type (bool) – Whether to include Content-Type header.

  • include_accept (bool) – Whether to include Accept header.

  • include_authorization (bool) – Whether to include Authorization header.

Return type:

SearchStrategy[dict[str, str]]

Returns:

A Hypothesis strategy that generates dictionaries of HTTP headers.

Example

>>> from hypothesis import strategies as st
>>> # Generate custom headers
>>> header_strategy = generate_headers(
...     header_specs={"X-Request-ID": str, "X-Trace-ID": str},
...     include_accept=True,
... )
>>> # Generate only standard headers
>>> standard_headers = generate_headers(
...     include_content_type=True,
...     include_accept=True,
... )
pytest_routes.generation.headers.generate_optional_headers(required_headers=None, optional_headers=None)[source]

Generate headers with required and optional fields.

This is useful for testing routes where some headers are mandatory and others are optional.

Parameters:
  • required_headers (dict[str, type] | None) – Headers that must always be present.

  • optional_headers (dict[str, type] | None) – Headers that may or may not be included.

Return type:

SearchStrategy[dict[str, str]]

Returns:

A Hypothesis strategy that generates dictionaries of HTTP headers.

Example

>>> header_strategy = generate_optional_headers(
...     required_headers={"Authorization": str},
...     optional_headers={"X-Request-ID": str, "X-Trace-ID": str},
... )

Header generation provides strategies for HTTP headers:

from pytest_routes import (
    generate_headers,
    generate_optional_headers,
    register_header_strategy,
)

# Generate required headers
headers = generate_headers(["Authorization", "Content-Type"])

# Generate optional headers (may or may not be present)
optional = generate_optional_headers(["X-Request-ID", "X-Trace-ID"])

# Register a custom header strategy
from hypothesis import strategies as st
register_header_strategy(
    "Authorization",
    st.just("Bearer test-token-12345")
)

Advanced Usage

Pydantic Model Strategies

pytest-routes automatically generates strategies for Pydantic models:

from pydantic import BaseModel, Field
from pytest_routes import strategy_for_type

class User(BaseModel):
    id: int = Field(ge=1)
    name: str = Field(min_length=1, max_length=100)
    email: str

# Automatically creates a valid User strategy
user_strategy = strategy_for_type(User)
user = user_strategy.example()

Dataclass Strategies

Dataclasses are also supported:

from dataclasses import dataclass
from pytest_routes import strategy_for_type

@dataclass
class Point:
    x: float
    y: float

point_strategy = strategy_for_type(Point)
point = point_strategy.example()

Constrained Strategies

For more control, register constrained strategies:

from hypothesis import strategies as st
from pytest_routes import register_strategy

# Positive integers only
register_strategy(
    int,
    st.integers(min_value=0, max_value=10000),
    override=True
)

# Email-like strings
class Email(str):
    pass

register_strategy(
    Email,
    st.emails()
)

See Also