# Testing the API and the Contract — API Design and Versioning

Source: https://www.geekswithgeeks.com/en/api-design/qg-testing

> Combine unit, integration, contract and breaking-change tests.

## Layers of confidence

Test an API at several levels. **Unit tests** cover business logic. **Integration tests** call the real HTTP endpoints against a test database and check status codes, headers and bodies, including error cases and the auth/ownership rules. **Contract tests** verify that responses match the OpenAPI schema (and, with **consumer-driven contracts** such as Pact, that the API still satisfies what real consumers expect). A **breaking-change check** in CI diffs the new spec against the released one (tools like `oasdiff` do this) and fails the pull request on incompatible changes unless a new version is intended. Add tests for pagination edges (empty, last page), idempotent retries, rate-limit responses and webhook signature verification. Run a smoke test against production after each deploy.

## API tests in pytest (illustrative)

These check the behaviours demonstrated earlier in the course against a test client. `client` is your framework's test client fixture.

```python
def test_idempotent_payment(client):
    h = {"Idempotency-Key": "k-1"}
    first = client.post("/v1/payments", json={"amount": 500}, headers=h)
    again = client.post("/v1/payments", json={"amount": 500}, headers=h)
    assert first.status_code == 201 and again.status_code == 200
    assert first.json() == again.json()
    assert again.headers["Idempotent-Replayed"] == "true"

def test_other_users_order_is_404(client, token_a, order_of_b):
    r = client.get(f"/v2/orders/{order_of_b}", headers={"Authorization": f"Bearer {token_a}"})
    assert r.status_code == 404

def test_last_page_has_null_cursor(client):
    assert client.get("/v1/items?limit=100").json()["next_cursor"] is None
```

## Pin versions in tests

Keep a test suite per supported version. When you retire a version, delete its tests with the code, not before.

**Quiz:** What does a CI breaking-change check do?

- [ ] Deletes old versions
- [ ] Deploys to production
- [x] Compares the new spec with the released one and flags incompatible changes
- [ ] Encrypts the spec

*Answer:* Compares the new spec with the released one and flags incompatible changes. Automatic diffing stops accidental breaking changes from reaching users.
