Skip to main content

image image image Test Docs

pytest-httpchain

A pytest plugin for testing HTTP endpoints.

Overview

pytest-httpchain is an integration testing framework for HTTP APIs based on httpx lib.
It aims at helping with common HTTP API testing scenarios, where user needs to make several calls in specific order using data obtained along the way, like auth tokens or resource ids.

Why pytest-httpchain?

Testing HTTP APIs with plain pytest often leads to these pain points:

  • Boilerplate accumulates — Every test repeats the same setup: create client, set headers, make request, parse response, assert. The actual test intent gets buried.
  • Data threading is manual — When one call returns a token or ID needed by the next, you end up with fragile helper functions passing state around.
  • Common patterns get copy-pasted — Auth flows, base URLs, shared headers end up duplicated across test files. Fixtures might help, but they are not designed for that.
  • Code reviews are noisy — The actual test logic is rarely clear because of all the boilerplate and helpers, following changes gets overwhelming quickly.

pytest-httpchain offers a more structured approach.

Features

Declarative JSON format

Test scenarios are JSON documents that describe what to test, not how. No setup code to scroll through — the request and assertions are right there.

$include / $merge with deep merging

Reuse arbitrary parts of your scenarios with JSONRef. Properties merge with type checking, so you can compose scenarios from shared fragments (auth flows, common headers, base URLs). $ref is a legacy alias that still works, but prefer $include/$merge: VS Code gives $ref its own JSON Schema handling, which fights the editor integration below.

Multi-stage execution

Each scenario contains 1+ stages executed in order. One stage failure stops the chain. Use always_run for cleanup stages that should execute regardless.

Common data context

A key-value store persists throughout scenario execution. Variables, fixtures, and saved response data all live here. Use template expressions ({{ var }}) in any request value — substitution happens dynamically before each stage. (Dict keys are not substituted; HTTPCHAIN029 flags a template in a key.)

Response processing

  • JMESPath — Extract values from JSON responses directly
  • JSON Schema — Validate response structure against a schema
  • User functions — Call Python functions for custom extraction, verification, or authentication

Full pytest integration

Markers, fixtures, parametrization, and other plugins work as expected. You're not locked into a separate ecosystem.

Quick Start

Create a JSON test file named like test_<name>.<suffix>.json (default suffix is http):

{
    "substitutions": [
        {
            "vars": {
                "user_id": 1
            }
        }
    ],
    "stages": {
        "get_user": {
            "request": {
                "url": "https://api.example.com/users/{{ user_id }}"
            },
            "response": [
                {
                    "verify": {
                        "status": 200
                    }
                },
                {
                    "save": {
                        "jmespath": {
                            "user_name": "user.name"
                        }
                    }
                }
            ]
        },
        "update_user": {
            "fixtures": ["now_utc"],
            "request": {
                "url": "https://api.example.com/users/{{ user_id }}",
                "method": "PUT",
                "body": {
                    "json": {
                        "user": {
                            "name": "{{ user_name }}_updated",
                            "timestamp": "{{ str(now_utc) }}"
                        }
                    }
                }
            },
            "response": [
                {
                    "verify": {
                        "status": 200
                    }
                }
            ]
        },
        "cleanup": {
            "always_run": true,
            "request": {
                "url": "https://api.example.com/cleanup",
                "method": "POST"
            }
        }
    }
}

The one stage above that needs Python is update_user, which asks for a now_utc fixture — ordinary pytest fixtures, resolved from your conftest.py:

# conftest.py
import pytest
from datetime import datetime


@pytest.fixture
def now_utc():
    return datetime.now()

Scenario we created:

  • common data context is seeded with the first variable user_id
  • get_user
    url is assembled using user_id variable from common data context
    HTTP GET call is made
    we verify the call returned code 200
    assuming JSON body is returned, we extract a value by JMESPath expression user.name and save it to common data context under user_name key
  • update_user
    now_utc fixture value is injected into common data context
    url is assembled using user_id variable from common data context
    we create JSON body in place using values from common data context, note that now_utc is converted to string in place
    HTTP PUT call with body is made
    we verify the call returned code 200
  • cleanup
    finalizing call meant for graceful exit
    always_run parameter means this stage will be executed regardless of errors in previous stages

For detailed usage guide see the full documentation, and the CLI reference for the offline authoring commands.

Installation

Install normally via package manager of your choice from PyPi:

pip install pytest-httpchain

or directly from Github, in case you need a particular ref:

pip install 'git+https://github.com/aeresov/pytest-httpchain@main'

Configuration

  • Test file discovery is based on this name pattern: test_<name>.<suffix>.json. The suffix is configurable via the httpchain_suffix pytest ini option, default value is http.
  • $include/$merge instructions (and their legacy alias $ref) can point to other files using relative paths; absolute paths are rejected for security, and every reference must resolve inside the root path (pytest's rootdir when collecting; --root-path for the CLI). You can limit the depth of relative path traversal using the httpchain_ref_parent_traversal_depth ini option, default value is 3.
  • Template expressions support list/dict comprehensions. You can limit the maximum comprehension length using the httpchain_max_comprehension_length ini option, default value is 50000.
  • Parallel stage iterations (repeat/foreach) have a safety limit configurable via the httpchain_max_parallel_iterations ini option, default value is 10000.

HAR export

Pass --httpchain-output-dir DIR on the pytest command line to write an HAR file (and a "HAR File" report section) capturing each test's HTTP traffic:

pytest --httpchain-output-dir ./har-output

HAR files contain full requests/responses including credential headers and saved tokens — nothing is redacted, so scrub them before sharing. Bodies are embedded complete and uncapped (binary bodies grow ~33% as base64), so scenarios that transfer large payloads produce large .har files. See the HAR export docs.

AI agent support

pytest-httpchain ships a scenario validator to help AI coding agents (and humans) author and check test scenarios.

Scenario validation

Validate scenario files for structure and common problems — undefined variables, variables referenced before they are saved (data-flow ordering), duplicate stage names, fixture/variable conflicts, no-op verify steps, and contradictory body checks:

uvx pytest-httpchain validate tests/test_login.http.json

Each finding carries a stable diagnostic code (HTTPCHAINxxx) and a severity — the full code reference is on the docs site, along with a recipe for filtering the ScenarioValidationWarning warnings the same checks emit at pytest collection. It exits non-zero when any file is invalid, so it doubles as a CI gate. Use --format json for machine-readable output (editor/CI integration):

uvx pytest-httpchain validate --format json tests/test_login.http.json

The same checks also run automatically at pytest collection time — semantic errors fail collection and warnings are reported — so pytest --collect-only validates every scenario in your suite.

For deeper, opt-in checks, add --deep: it imports your module:func references to confirm they resolve, checks their call signatures (including the injected response for save/verify functions), and verifies referenced files and schemas exist. Because it imports your code it is never run at collection time; pair it with --strict to fail CI on any warning, and --syspath to add import roots:

uvx pytest-httpchain validate --deep --strict tests/test_login.http.json

Editor schema

A JSON Schema is published for as-you-type validation and autocomplete. Reference it from your test files:

{
    "$schema": "https://aeresov.github.io/pytest-httpchain/schema/scenario.schema.json"
}

The hosted schema at the unversioned URL tracks the main branch (it is redeployed on every push, so it may describe unreleased changes). To pin the schema for a release, use its versioned URL:

{
    "$schema": "https://aeresov.github.io/pytest-httpchain/schema/v0.14.0/scenario.schema.json"
}

or emit the schema matching your installed version locally:

uvx pytest-httpchain schema > scenario.schema.json

Inspecting scenarios

More read-only commands help author and debug scenarios offline — no network, no test run:

# Print a scenario with all $ref/$include/$merge inlined and deep-merged
uvx pytest-httpchain resolve tests/test_login.http.json

# Summarize stages and the variable data-flow (which stage saves what, who consumes it)
uvx pytest-httpchain show tests/test_login.http.json

# Render the stage data-flow as a Mermaid flowchart
uvx pytest-httpchain graph tests/test_login.http.json

Documentation

Thanks

This project was inspired by Tavern and pytest-play.

httpx does comms.
Pydantic keeps structure.
simpleeval powers templates.
pytest-datadir saved me a lot of elbow grease while testing.

Release files for pytest-httpchain 0.15.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-httpchain 0.15.0
File Size Uploaded
pytest_httpchain-0.15.0.tar.gz 81.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-httpchain 0.15.0
File Interpreter ABI Platform
pytest_httpchain-0.15.0-py3-none-any.whl Python 3 none any Details

Total release size: 178.6 kB

Release files / pytest_httpchain-0.15.0.tar.gz

Download URL pytest_httpchain-0.15.0.tar.gz
Size 81.3 kB
Tags Source
SHA-256 checksum
How to use checksums
b88c3c33c5958c20b7e70f702afbfdfd2bd12a676de1c6e8501817c128582d22
BLAKE2b-256 checksum
How to use checksums
e37b4639ed40bb540db0ef022bd094409d69da9f7b4c42b3c4a805e6724e9f57
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release files / pytest_httpchain-0.15.0-py3-none-any.whl

Download URL pytest_httpchain-0.15.0-py3-none-any.whl
Size 97.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4e9072a54074750e705ecc17e37c6d2c24ca5111da71691437f41ef6741ce7d0
BLAKE2b-256 checksum
How to use checksums
323a6a08cbc8fcf06c9e95774798c60e7969cdda1e46a4d3a31d4ae807385e65
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release history Release notifications | RSS feed

0.15.1

2 release files

This release

0.15.0 This release

2 release files

0.14.3

2 release files

0.14.1

2 release files

0.14.0

2 release files

0.13.0

2 release files

0.12.0

2 release files

0.11.0

2 release files

0.10.0

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.4

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page