Skip to main content

Add your description here

Project description

Accord

Accord is a Python library for consumer-driven contract testing between web services, you define a Contract once using Pydantic models and an @endpoint decorator, which then automatically takes care of the rest for you:

  • Spins up a mock http-server using werkzeug
  • Generates a pact compatible JSON contract file

This ensures both sides of a service boundary stay in sync without requiring a shared test environment.

Why not Pact?

Pact is definitely the industry-standard library, and if you are working with multiple different languages or environments then it is the better choice. But if your stack is Python-only, Pact comes with a lot of overhead; that you may not need:

  • A separate DSL
    • Pact has its own way of defining contracts that lives outside your existing code. Accord uses the Pydantic models you're already writing.
  • The broker
    • Pact strongly encourages (and in practice, requires) a Pact broker to share contracts between teams. Accord generates a JSON file and you share it however you want: a shared repo, a CI artifact, or S3.
  • Usage time
    • Getting Pact fully set up across two services takes time. Accord is designed to be useful in an afternoon.

Accord is not supposed to replace Pact, it is for simplicity and less overhead. In reality, Pact is more useful across the board.

Learn how to use Accord

Before you learn how to use Accord, here are some simple diagrams that showcase how our Contracts & Systems work.

Contracts

sequenceDiagram
    participant Dev as Developer
    participant Endpoint as @endpoint
    participant Contract as Contract Class

    Dev->>Endpoint: Decorates method with http_method and path
    Endpoint->>Endpoint: Extracts return type from type hints
    Endpoint->>Endpoint: Attaches EndpointMetadata to function
    Dev->>Contract: Defines Contract subclass
    Contract->>Contract: __init_subclass__ runs
    Contract->>Contract: Scans for functions with accord_endpoint
    Contract->>Contract: Registers each into interactions dict

Consumer Flow

sequenceDiagram
    participant Consumer
    participant MockServer as Accord MockServer
    participant File as accords/*.json

    Consumer->>MockServer: MockServer(Contract)
    MockServer->>MockServer: Register routes from interactions
    MockServer->>MockServer: Start Werkzeug server in background thread
    Consumer->>MockServer: given("get_user").example(UserResponse(...))
    MockServer->>MockServer: Store example in examples dict
    Consumer->>MockServer: HTTP GET /users/1
    MockServer->>MockServer: Match route, look up example
    MockServer->>Consumer: Return example as JSON response
    Consumer->>Consumer: Assert response
    Consumer->>MockServer: Exit context manager
    MockServer->>File: write_contract() — Pact-compatible JSON
    MockServer->>MockServer: Shutdown server

Producer Verification

sequenceDiagram
    participant File as accords/*.json
    participant Verifier as Accord ContractVerifier
    participant Provider

    Verifier->>File: read_contract()
    File->>Verifier: ParsedContract with interactions
    loop For each interaction
        Verifier->>Provider: HTTP request using interaction method and path
        Provider->>Verifier: Actual response
        Verifier->>Verifier: Parse response body
        loop For each field in matchingRules
            Verifier->>Verifier: Map JSON type to Python type
            Verifier->>Verifier: isinstance check
            alt Type mismatch
                Verifier->>Verifier: Raise ValueError
            end
        end
    end
    Verifier->>Verifier: All interactions verified

Now with all of those flows out of the way, the example may make more sense that we provide here.

A basic contract is defined as such:

from pydantic import BaseModel
from core.contract import Contract, endpoint

class UserResponse(BaseModel):
    id: int
    name: str

class GetUserContract(Contract):
    consumer = "order-service"
    producer = "user-service"

    @endpoint(http_method="GET", path="/users/{id}")
    def get_user(self, user_id: int) -> UserResponse: ...

Once we have this contract defined, we use it to spin up a MockServer to test the Consumer:

import httpx
from server.mock import MockServer

with MockServer(GetUserContract) as server:
    server.given("get_user").example(UserResponse(id=1, name="Alice"))

    response = httpx.get("http://127.0.0.1:5000/users/1")
    assert response.status_code == 200
    assert response.json()["name"] == "Alice"

This writes the contract to ./accords/ once the ContextManager exits. The other side of the testing flow is producer, which you can do as such:

from pathlib import Path
from verification.verifier import ContractVerifier

# This assumes your server is running on 8080

verifier = ContractVerifier(
    contract_path=Path("accords/order-service-user-service.json"),
    base_url="http://127.0.0.1:8080",
)
verifier.verify()

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

accord_contracts-0.1.0.tar.gz (6.7 kB view details)

Uploaded Source

Built Distribution

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

accord_contracts-0.1.0-py3-none-any.whl (8.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: accord_contracts-0.1.0.tar.gz
  • Upload date:
  • Size: 6.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for accord_contracts-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5db9dd0676f72e5e1dec94ddcbe16072e85fc264c16d0fb088af5433966edb4c
MD5 79343bbcdbcdb06ef0de08853584c97d
BLAKE2b-256 d9857150f25832e5d64ab41ef3f516e3c371a83637cba3abb0e82fe4965b0c39

See more details on using hashes here.

File details

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

File metadata

  • Download URL: accord_contracts-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for accord_contracts-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 eb2c17d05694e3f4a6906f97b3611bfb851996e5073f61ac483b4b2a3548f1c9
MD5 2a4b2b0aa355cdade6ec3409d991066d
BLAKE2b-256 7683b3988db8f84ebe45e3e189ebfbc1f669e084c72494a9097de3a0480ebce5

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