- Python 100%
| accord.egg-info | ||
| accord_contracts.egg-info | ||
| accords | ||
| core | ||
| serialization | ||
| server | ||
| test | ||
| verification | ||
| pyproject.toml | ||
| pyrightconfig.json | ||
| README.md | ||
| uv.lock | ||
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:
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
Consumer Flow
Producer Verification
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()