[](https://pypi.python.org/pypi/pytest-httpchain)
[](https://github.com/aeresov/pytest-httpchain/blob/main/LICENSE)
[](https://pypi.python.org/pypi/pytest-httpchain)
# pytest-httpchain
A pytest plugin for testing HTTP endpoints.
## Overview
`pytest-httpchain` is an integration testing framework for HTTP APIs based on battle-hardened [requests](https://requests.readthedocs.io) 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.
## Installation
Install normally via package manager of your choice from PyPi:
```bash
pip install pytest-httpchain
```
or directly from Github, in case you need a particular ref:
```bash
pip install 'git+https://github.com/aeresov/pytest-httpchain@main'
```
### Optional dependencies
The following optional dependencies are available:
- `mcp`: installs MCP server package and its starting script. Details in [MCP Server](#mcp-server).
## Features
### Pytest integration
Most of pytest magic can be used: markers, fixtures, other plugins.
> NOTE: parametrization is not yet implemented, therefore `parametrize` marker won't have any effect.
### Declarative format
Test scenarios are written declaratively in JSON files.
`pytest-httpchain` supports JSONRef, so use can reuse arbitrary parts of your scenarios with `$ref` directive.
Properties are merged in a greedy way with type checking.
### Multi-stage tests
Each test scenario contains 1+ stages; each stage is a single HTTP call.
`pytest-httpchain` executes stages in the order they are listed in scenario file; one stage failure stops the execution chain.
### Common data context and variable substitution
`pytest-httpchain` maintains key-value data storage throughout the execution.
This storage ("common data context") is populated with declared variables, fixtures and data saved by stages. The data remains there throughout the scenario execution.
Writing scenarios, you can use Jinja-style expressions like `"{{ var }}"` for JSON values. `pytest-httpchain` does variable substitution dynamically right before executing a stage, and uses common data context keys as variables in these expressions.
Values from common data context also might be verified during verified/asserted.
### User functions
`pytest-httpchain` can import and call regular python functions:
- to extract data from HTTP response
- to verify HTTP response and values in common data context
- to provide [custom authentication for requests](https://requests.readthedocs.io/en/latest/user/advanced/#custom-authentication)
### JMESPath support
`pytest-httpchain` can extract values from JSON responses using JMESPath expressions directly.
### JSON schema support
`pytest-httpchain` can verify JSON reponses against user-defined JSON schema.
## Quick Start
Create a JSON test file named like `test_<name>.<suffix>.json` (default suffix is `http`):
```python
# conftest.py
import pytest
from datetime import datetime
@pytest.fixture
def now_utc():
return datetime.now()
```
```json
{
"vars": {
"user_id": 1
},
"stages": [
{
"name": "get_user",
"request": {
"url": "https://api.example.com/users/{{ user_id }}"
},
"response": [
{
"verify": {
"status": 200
}
},
{
"save": {
"vars": {
"user_name": "user.name"
}
}
}
]
},
{
"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
}
}
]
},
{
"name": "cleanup",
"always_run": true,
"request": {
"url": "https://api.example.com/cleanup",
"method": "POST"
}
}
]
}
```
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 examples see [USAGE.md](USAGE.md).
## Configuration
- Test file discovery is based on this name pattern: `test_<name>.<suffix>.json`.
The `suffix` is configurable as pytest ini option, default value is **http**.
- `$ref` instructions can point to other files; absolute and relative paths are supported.
You can limit the depth of relative path traversal using `ref_parent_traversal_depth` ini option, default value is **3**.
## MCP Server
`pytest-httpchain` includes an MCP (Model Context Protocol) server to aid AI code assistants.
### Installation
The optional dependency `mcp` installs MCP server's package and `pytest-httpchain-mcp` script.
Use this script as call target for your MCP configuration.
Claude Code `.mcp.json` example:
```json
{
"mcpServers": {
"pytest-httpchain": {
"type": "stdio",
"command": "uv",
"args": ["run", "pytest-httpchain-mcp"],
"env": {}
}
}
}
```
### Features
The MCP server provides:
- **Scenario validation** - validate test scenario and scan for possible problems
## Documentation
- [Usage Examples](USAGE.md) - Practical code examples
- [Full Documentation](https://aeresov.github.io/pytest-httpchain) - Complete guide
- [Changelog](CHANGELOG.md) - Release notes
## Thanks
`pytest-httpchain` was heavily inspired by [Tavern](https://github.com/taverntesting/tavern) and [pytest-play](https://github.com/davidemoro/pytest-play).
[requests](https://requests.readthedocs.io) does the comms.
[Pydantic](https://docs.pydantic.dev) keeps the structure.
[pytest-order](https://github.com/pytest-dev/pytest-order) powers the chaining.
[pytest-datadir](https://github.com/gabrielcnr/pytest-datadir) saved me a lot of elbow grease.
Raw data
{
"_id": null,
"home_page": null,
"name": "pytest-httpchain",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.13",
"maintainer_email": null,
"keywords": "testing, pytest, requests",
"author": "Alexander Eresov",
"author_email": "Alexander Eresov <aeresov@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/2e/60/6eccef3963245c007dc387d3530882e6b1309fba5972bc4f6b921dc8100b/pytest_httpchain-0.1.2.tar.gz",
"platform": null,
"description": "[](https://pypi.python.org/pypi/pytest-httpchain)\n[](https://github.com/aeresov/pytest-httpchain/blob/main/LICENSE)\n[](https://pypi.python.org/pypi/pytest-httpchain)\n\n# pytest-httpchain\n\nA pytest plugin for testing HTTP endpoints.\n\n## Overview\n\n`pytest-httpchain` is an integration testing framework for HTTP APIs based on battle-hardened [requests](https://requests.readthedocs.io) lib. \nIt 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.\n\n## Installation\n\nInstall normally via package manager of your choice from PyPi:\n\n```bash\npip install pytest-httpchain\n```\n\nor directly from Github, in case you need a particular ref:\n\n```bash\npip install 'git+https://github.com/aeresov/pytest-httpchain@main'\n```\n\n### Optional dependencies\n\nThe following optional dependencies are available:\n\n- `mcp`: installs MCP server package and its starting script. Details in [MCP Server](#mcp-server).\n\n## Features\n\n### Pytest integration\n\nMost of pytest magic can be used: markers, fixtures, other plugins. \n\n> NOTE: parametrization is not yet implemented, therefore `parametrize` marker won't have any effect.\n\n### Declarative format\n\nTest scenarios are written declaratively in JSON files. \n`pytest-httpchain` supports JSONRef, so use can reuse arbitrary parts of your scenarios with `$ref` directive. \nProperties are merged in a greedy way with type checking.\n\n### Multi-stage tests\n\nEach test scenario contains 1+ stages; each stage is a single HTTP call. \n`pytest-httpchain` executes stages in the order they are listed in scenario file; one stage failure stops the execution chain.\n\n### Common data context and variable substitution\n\n`pytest-httpchain` maintains key-value data storage throughout the execution. \nThis storage (\"common data context\") is populated with declared variables, fixtures and data saved by stages. The data remains there throughout the scenario execution. \nWriting scenarios, you can use Jinja-style expressions like `\"{{ var }}\"` for JSON values. `pytest-httpchain` does variable substitution dynamically right before executing a stage, and uses common data context keys as variables in these expressions. \nValues from common data context also might be verified during verified/asserted.\n\n### User functions\n\n`pytest-httpchain` can import and call regular python functions:\n\n- to extract data from HTTP response\n- to verify HTTP response and values in common data context\n- to provide [custom authentication for requests](https://requests.readthedocs.io/en/latest/user/advanced/#custom-authentication)\n\n### JMESPath support\n\n`pytest-httpchain` can extract values from JSON responses using JMESPath expressions directly.\n\n### JSON schema support\n\n`pytest-httpchain` can verify JSON reponses against user-defined JSON schema.\n\n## Quick Start\n\nCreate a JSON test file named like `test_<name>.<suffix>.json` (default suffix is `http`):\n\n```python\n# conftest.py\nimport pytest\nfrom datetime import datetime\n\n@pytest.fixture\ndef now_utc():\n return datetime.now()\n```\n\n```json\n{\n \"vars\": {\n \"user_id\": 1\n },\n \"stages\": [\n {\n \"name\": \"get_user\",\n \"request\": {\n \"url\": \"https://api.example.com/users/{{ user_id }}\"\n },\n \"response\": [\n {\n \"verify\": {\n \"status\": 200\n }\n },\n {\n \"save\": {\n \"vars\": {\n \"user_name\": \"user.name\"\n }\n }\n }\n ]\n },\n {\n \"name\": \"update_user\",\n \"fixtures\": [\"now_utc\"],\n \"request\": {\n \"url\": \"https://api.example.com/users/{{ user_id }}\",\n \"method\": \"PUT\",\n \"body\": {\n \"json\": {\n \"user\": {\n \"name\": \"{{ user_name }}_updated\",\n \"timestamp\": \"{{ str(now_utc) }}\"\n }\n }\n }\n },\n \"response\": [\n {\n \"verify\": {\n \"status\": 200\n }\n }\n ]\n },\n {\n \"name\": \"cleanup\",\n \"always_run\": true,\n \"request\": {\n \"url\": \"https://api.example.com/cleanup\",\n \"method\": \"POST\"\n }\n }\n ]\n}\n```\n\nScenario we created:\n\n- common data context is seeded with the first variable `user_id`\n- **get_user** \n url is assembled using `user_id` variable from common data context \n HTTP GET call is made \n we verify the call returned code 200 \n 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\n- **update_user** \n `now_utc` fixture value is injected into common data context \n url is assembled using `user_id` variable from common data context \n we create JSON body in place using values from common data context, note that `now_utc` is converted to string in place \n HTTP PUT call with body is made \n we verify the call returned code 200 \n- **cleanup** \n finalizing call meant for graceful exit \n `always_run` parameter means this stage will be executed regardless of errors in previous stages\n\nFor detailed examples see [USAGE.md](USAGE.md).\n\n## Configuration\n\n- Test file discovery is based on this name pattern: `test_<name>.<suffix>.json`. \n The `suffix` is configurable as pytest ini option, default value is **http**.\n- `$ref` instructions can point to other files; absolute and relative paths are supported. \n You can limit the depth of relative path traversal using `ref_parent_traversal_depth` ini option, default value is **3**.\n\n## MCP Server\n\n`pytest-httpchain` includes an MCP (Model Context Protocol) server to aid AI code assistants.\n\n### Installation\n\nThe optional dependency `mcp` installs MCP server's package and `pytest-httpchain-mcp` script. \nUse this script as call target for your MCP configuration.\n\nClaude Code `.mcp.json` example:\n\n```json\n{\n \"mcpServers\": {\n \"pytest-httpchain\": {\n \"type\": \"stdio\",\n \"command\": \"uv\",\n \"args\": [\"run\", \"pytest-httpchain-mcp\"],\n \"env\": {}\n }\n }\n}\n```\n\n### Features\n\nThe MCP server provides:\n\n- **Scenario validation** - validate test scenario and scan for possible problems\n\n## Documentation\n\n- [Usage Examples](USAGE.md) - Practical code examples\n- [Full Documentation](https://aeresov.github.io/pytest-httpchain) - Complete guide\n- [Changelog](CHANGELOG.md) - Release notes\n\n## Thanks\n\n`pytest-httpchain` was heavily inspired by [Tavern](https://github.com/taverntesting/tavern) and [pytest-play](https://github.com/davidemoro/pytest-play). \n[requests](https://requests.readthedocs.io) does the comms. \n[Pydantic](https://docs.pydantic.dev) keeps the structure. \n[pytest-order](https://github.com/pytest-dev/pytest-order) powers the chaining. \n[pytest-datadir](https://github.com/gabrielcnr/pytest-datadir) saved me a lot of elbow grease. \n",
"bugtrack_url": null,
"license": null,
"summary": "pytest plugin for HTTP testing using JSON files",
"version": "0.1.2",
"project_urls": null,
"split_keywords": [
"testing",
" pytest",
" requests"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "a5f672ba20c40a2e9ec6b81663a3e919e4781c4f36a026c7fd6dedeb4e9fca45",
"md5": "c299f2619a4c9d13d7adaa687d7e23fa",
"sha256": "c73e20663380bbab7b4f092332ba708e1f05079c8d0897ee5badb27c21dad4aa"
},
"downloads": -1,
"filename": "pytest_httpchain-0.1.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "c299f2619a4c9d13d7adaa687d7e23fa",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.13",
"size": 18348,
"upload_time": "2025-08-16T06:15:25",
"upload_time_iso_8601": "2025-08-16T06:15:25.082496Z",
"url": "https://files.pythonhosted.org/packages/a5/f6/72ba20c40a2e9ec6b81663a3e919e4781c4f36a026c7fd6dedeb4e9fca45/pytest_httpchain-0.1.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "2e606eccef3963245c007dc387d3530882e6b1309fba5972bc4f6b921dc8100b",
"md5": "1bb863400efab059a62f254c7833b343",
"sha256": "4f7e8d18d362f64364d58df532f48def8871381cb4ccdbdd6c1f2046ab0332fe"
},
"downloads": -1,
"filename": "pytest_httpchain-0.1.2.tar.gz",
"has_sig": false,
"md5_digest": "1bb863400efab059a62f254c7833b343",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.13",
"size": 13996,
"upload_time": "2025-08-16T06:15:28",
"upload_time_iso_8601": "2025-08-16T06:15:28.247472Z",
"url": "https://files.pythonhosted.org/packages/2e/60/6eccef3963245c007dc387d3530882e6b1309fba5972bc4f6b921dc8100b/pytest_httpchain-0.1.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-16 06:15:28",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "pytest-httpchain"
}