Skip to content

Example: requests Client and FastAPI Provider with XML Contract Testing

This example demonstrates contract testing between a synchronous requests-based client (consumer) and a FastAPI web server (provider) where the payload format is XML rather than JSON. It is designed to be pedagogical, highlighting both the standard Pact contract testing workflow and the specific constraints that arise when working with XML.

Overview

  • Consumer: Synchronous HTTP client using requests, parsing XML with xml.etree.ElementTree
  • Provider: FastAPI web server returning XML responses
  • Consumer Tests: Contract definition and consumer testing with Pact
  • Provider Tests: Provider verification against contracts

Use the above links to view additional documentation within.

What This Example Demonstrates

Consumer Side

  • Synchronous HTTP client implementation with requests
  • Parsing XML responses using the standard library xml.etree.ElementTree module
  • Consumer contract testing with Pact mock servers
  • Setting request headers (e.g., Accept: application/xml) using .with_header() as a separate chain step

Provider Side

  • FastAPI web server returning application/xml responses built with xml.etree.ElementTree
  • Provider verification against consumer contracts
  • Provider state setup and teardown for isolated, repeatable verification

Testing Patterns

  • Independent consumer and provider testing
  • Contract-driven development workflow
  • Using the pact.xml builder to apply Pact matchers to XML bodies
  • How matcher-based contracts are more flexible than exact XML string matching

XML Matchers

Unlike JSON, XML bodies cannot be expressed as a plain Python dict of field-matcher pairs. Instead, use the pact.xml builder, which constructs the body description from nested xml.element() calls:

from pact import match, xml

response = xml.body(
    xml.element("user",
        xml.element("id", match.int(123)),
        xml.element("name", match.str("Alice")),
    )
)

Pass the result to .with_body() with content_type="application/xml". The Pact FFI detects that the body is JSON, generates the example XML, and registers matching rules for each annotated element. The resulting contract will match any XML response where <id> contains an integer and <name> contains a string and not just the specific example values.

For attribute matchers, pass matcher objects via the attrs keyword argument:

xml.element("user", attrs={"id": match.int(1), "type": "activity"})

For repeating elements, chain .each() to add a type matching rule:

(
    xml.element(
        "items",
        xml.element("item", xml.element("id", match.int(1))).each(min=1, examples=2),
    )
)

For JSON-based examples using the same matchers, see requests_and_fastapi/.

Prerequisites

  • Python 3.10 or higher
  • A dependency manager (uv recommended, pip also works)

Running the Example

We recommend using uv to manage the virtual environment and dependencies. The following command will automatically set up the virtual environment, install dependencies, and execute the tests:

uv run --group test pytest

Using pip

If using the venv module, the required steps are:

  1. Create and activate a virtual environment:

    python -m venv .venv
    source .venv/bin/activate  # On macOS/Linux
    .venv\Scripts\activate     # On Windows
    
  2. Install dependencies:

    pip install -U pip  # Pip 25.1 is required
    pip install --group test -e .
    
  3. Run tests:

    pytest