Skip to content

Test provider

Provider contract tests using Pact.

This module demonstrates how to test a FastAPI provider (see provider.py) against a mock consumer using Pact. The mock consumer replays the requests defined by the consumer contract, and Pact validates that the provider responds as expected.

These tests show how provider verification ensures that the provider remains compatible with the consumer contract as the provider evolves. Provider state management is handled by mocking the database and using provider state endpoints. For more, see the Pact Provider Test section of the Pact documentation.

Attributes

ACTION_TYPE: TypeAlias = Literal['setup', 'teardown'] module-attribute

logger = logging.getLogger(__name__) module-attribute

Classes

Functions

app_server() -> str

Run the FastAPI server for provider verification.

RETURNS DESCRIPTION
str

The base URL of the running FastAPI server.

Source code in examples/http/requests_and_fastapi/test_provider.py
@pytest.fixture(scope="session")
def app_server() -> str:
    """
    Run the FastAPI server for provider verification.

    Returns:
        The base URL of the running FastAPI server.
    """
    hostname = "localhost"
    port = pact._util.find_free_port()  # noqa: SLF001
    Thread(
        target=uvicorn.run,
        args=(app,),
        kwargs={"host": hostname, "port": port},
        daemon=True,
    ).start()
    return f"http://{hostname}:{port}"

default_mock_db() -> dict[int, User]

Standard in-memory database for provider state mocking.

This function pre-populates a mock database with some default users. It is used by the provider state handlers to ensure that the database is in the correct state for each interaction.

RETURNS DESCRIPTION
dict[int, User]

A dictionary of user IDs to User objects for use in tests.

Source code in examples/http/requests_and_fastapi/test_provider.py
def default_mock_db() -> dict[int, User]:
    """
    Standard in-memory database for provider state mocking.

    This function pre-populates a mock database with some default users. It is
    used by the provider state handlers to ensure that the database is in the
    correct state for each interaction.

    Returns:
        A dictionary of user IDs to User objects for use in tests.
    """
    return {
        1: User(
            id=1,
            name="Alice",
            email="alice@example.com",
            created_on=datetime(2020, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
            ip_address="1.2.3.4",
            hobbies=["pact testing", "programming", "qa"],
            admin=False,
        ),
        2: User(
            id=2,
            name="Bob",
            email=None,  # Edge case: email is None
            created_on=datetime(1999, 12, 31, 23, 59, 59, tzinfo=timezone.utc),
            ip_address="",
            hobbies=[],
            admin=True,
        ),
        10: User(
            id=10,
            name="Charlie",
            email="charlie@example.com",
            created_on=datetime(2025, 8, 8, 8, 8, 8, tzinfo=timezone.utc),
            ip_address="3.4.5.6",
            hobbies=[""],
            admin=False,
        ),
        42: User(
            id=42,
            name="Dana",
            email="dana+test@example.com",
            created_on=datetime(2022, 2, 22, 2, 22, 22, tzinfo=timezone.utc),
            ip_address="255.255.255.255",
            hobbies=["edge", "case", "testing"],
            admin=False,
        ),
    }

mock_user_does_not_exist(action: Literal['setup', 'teardown'], parameters: dict[str, Any]) -> None

Mock the provider state where a user does not exist.

This handler sets up the provider so that a user with the given ID does not exist in the database. Used by Pact to ensure the provider is in the correct state for each interaction.

PARAMETER DESCRIPTION
action

The action to perform, either "setup" or "teardown".

TYPE: Literal['setup', 'teardown']

parameters

User information, must contain an id to guarantee absence in the database.

TYPE: dict[str, Any]

Source code in examples/http/requests_and_fastapi/test_provider.py
def mock_user_does_not_exist(
    action: Literal["setup", "teardown"],
    parameters: dict[str, Any],
) -> None:
    """
    Mock the provider state where a user does not exist.

    This handler sets up the provider so that a user with the given ID does not
    exist in the database. Used by Pact to ensure the provider is in the correct
    state for each interaction.

    Args:
        action:
            The action to perform, either "setup" or "teardown".

        parameters:
            User information, must contain an `id` to guarantee absence in the
            database.
    """
    logger.debug("mock_user_does_not_exist(%s, %r)", action, parameters)

    if "id" not in parameters:
        msg = "State must contain an 'id' field to mock user non-existence"
        raise ValueError(msg)

    uid = parameters["id"]

    if action == "setup":
        if user := UserDb.get(uid):
            UserDb.delete(user.id)
        return

    if action == "teardown":
        return

    msg = f"Unknown action: {action}"
    raise ValueError(msg)

mock_user_exists(action: Literal['setup', 'teardown'], parameters: dict[str, Any]) -> None

Mock the provider state where a user exists.

This handler sets up the provider so that a user with the given ID exists in the database. Used by Pact to ensure the provider is in the correct state for each interaction.

PARAMETER DESCRIPTION
action

The action to perform, either "setup" or "teardown".

TYPE: Literal['setup', 'teardown']

parameters

User information, including an id to guarantee presence in the database. Additional fields may be provided to override defaults.

TYPE: dict[str, Any]

Source code in examples/http/requests_and_fastapi/test_provider.py
def mock_user_exists(
    action: Literal["setup", "teardown"],
    parameters: dict[str, Any],
) -> None:
    """
    Mock the provider state where a user exists.

    This handler sets up the provider so that a user with the given ID exists in
    the database. Used by Pact to ensure the provider is in the correct state
    for each interaction.

    Args:
        action:
            The action to perform, either "setup" or "teardown".

        parameters:
            User information, including an `id` to guarantee presence in the
            database. Additional fields may be provided to override defaults.
    """
    logger.debug("mock_user_exists(%s, %r)", action, parameters)

    # We pre-populate the database with some data, and if a state requires
    # some specific data, ensure the user is present.
    db = default_mock_db()
    user = db[uid] if (uid := parameters.get("id")) in db else next(iter(db.values()))
    user = User(**{**dataclasses.asdict(user), **parameters})

    if action == "setup":
        UserDb.create(user)
        return

    if action == "teardown":
        with contextlib.suppress(KeyError):
            UserDb.delete(user.id)
        return

    msg = f"Unknown action: {action}"
    raise ValueError(msg)

start_fastapi_server(host: str, port: int) -> None

Source code in examples/http/requests_and_fastapi/test_provider.py
def start_fastapi_server(host: str, port: int) -> None:
    uvicorn.run(
        app,
        host=host,
        port=port,
    )

test_provider(app_server: str, pacts_path: Path) -> None

Test the provider against the mock consumer contract.

This test runs the Pact verifier against the FastAPI provider, using the contract generated by the consumer tests.

Provider state handlers are essential in Pact contract testing. They allow the provider to be set up in a specific state before each interaction is verified. For example, if a consumer expects a user to exist for a certain request, the provider state handler ensures the database is populated accordingly. This enables repeatable, isolated, and meaningful contract verification, as each interaction can be tested in the correct context without relying on global or persistent state.

In this example, the state handlers mock_user_exists and mock_user_does_not_exist are mapped to the states described in the contract. They are responsible for setting up (and tearing down) the in-memory database so that the provider can respond correctly to each request defined by the consumer contract.

For additional information on state handlers, see Verifier.state_handler.

Source code in examples/http/requests_and_fastapi/test_provider.py
def test_provider(app_server: str, pacts_path: Path) -> None:
    """
    Test the provider against the mock consumer contract.

    This test runs the Pact verifier against the FastAPI provider, using the
    contract generated by the consumer tests.

    Provider state handlers are essential in Pact contract testing. They allow
    the provider to be set up in a specific state before each interaction is
    verified. For example, if a consumer expects a user to exist for a certain
    request, the provider state handler ensures the database is populated
    accordingly. This enables repeatable, isolated, and meaningful contract
    verification, as each interaction can be tested in the correct context
    without relying on global or persistent state.

    In this example, the state handlers `mock_user_exists` and
    `mock_user_does_not_exist` are mapped to the states described in the
    contract. They are responsible for setting up (and tearing down) the
    in-memory database so that the provider can respond correctly to each
    request defined by the consumer contract.

    For additional information on state handlers, see
    [`Verifier.state_handler`][pact.verifier.Verifier.state_handler].
    """
    verifier = (
        Verifier("fastapi-provider")
        .add_source(pacts_path)
        .add_transport(url=app_server)
        .state_handler(
            {
                "the user exists": mock_user_exists,
                "the user doesn't exist": mock_user_does_not_exist,
            },
            teardown=True,
        )
    )

    verifier.verify()