References and Deep Merging¶
pytest-httpchain supports JSON references for reusing scenario components across files. References are resolved with deep merging, allowing you to compose scenarios from shared fragments.
$include / $merge vs $ref¶
Three directives are supported and work identically:
$include(recommended): Avoids conflicts with VS Code's JSON Schema validation$merge(recommended): Alias for$include, semantically clearer when merging properties$ref: Standard JSON Reference syntax, but may cause VS Code/IDE validation warnings
// Recommended - no VS Code conflicts
{ "$include": "common.json#/headers" }
{ "$merge": "base.json", "extra": "value" }
// Also works, but may show VS Code warnings
{ "$ref": "common.json#/headers" }
Basic Syntax¶
Reference another file:
Reference a specific key within a file:
File References¶
Same Directory¶
Relative Paths¶
Nested Directories¶
JSON Pointer References¶
Reference specific keys using JSON Pointer syntax:
common.json:
{
"headers": {
"default": {
"Content-Type": "application/json",
"Accept": "application/json"
},
"auth": {
"Authorization": "Bearer {{ token }}"
}
},
"requests": {
"login": {
"url": "https://api.example.com/login",
"method": "POST"
}
}
}
test_scenario.http.json:
{
"stages": [
{
"name": "login",
"request": {
"$ref": "common.json#/requests/login",
"headers": {
"$ref": "common.json#/headers/default"
}
}
}
]
}
Deep Merging¶
When a $ref is used alongside other properties, values are deep merged:
base.json:
{
"request": {
"url": "https://api.example.com",
"headers": {
"Content-Type": "application/json"
},
"timeout": 30
}
}
test_scenario.http.json:
{
"stages": [
{
"name": "custom_request",
"$ref": "base.json",
"request": {
"url": "https://api.example.com/custom",
"headers": {
"X-Custom": "value"
}
}
}
]
}
Resolved result:
{
"stages": [
{
"name": "custom_request",
"request": {
"url": "https://api.example.com/custom",
"headers": {
"Content-Type": "application/json",
"X-Custom": "value"
},
"timeout": 30
}
}
]
}
Merge Rules¶
- Objects: Recursively merged (properties combined)
- Arrays: Replaced entirely (no element merging)
- Scalars: Local value overrides referenced value
- Type mismatch: Local value wins
Composing Scenarios¶
Shared Stage Fragments¶
fragments/stages.json:
{
"login": {
"name": "login",
"request": {
"url": "https://api.example.com/auth/login",
"method": "POST",
"body": {
"json": {
"username": "{{ username }}",
"password": "{{ password }}"
}
}
},
"response": [
{"verify": {"status": 200}},
{"save": {"jmespath": {"token": "access_token"}}}
]
},
"logout": {
"name": "logout",
"always_run": true,
"request": {
"url": "https://api.example.com/auth/logout",
"method": "POST",
"headers": {
"Authorization": "Bearer {{ token }}"
}
}
}
}
test_workflow.http.json:
{
"substitutions": [
{
"vars": {
"username": "testuser",
"password": "testpass"
}
}
],
"stages": [
{
"$ref": "fragments/stages.json#/login"
},
{
"name": "do_something",
"request": {
"url": "https://api.example.com/action",
"headers": {
"Authorization": "Bearer {{ token }}"
}
}
},
{
"$ref": "fragments/stages.json#/logout"
}
]
}
Shared Configuration¶
config/defaults.json:
{
"ssl": {
"verify": true
},
"auth": "auth_module:get_default_auth",
"substitutions": [
{
"vars": {
"base_url": "https://api.example.com",
"timeout": 30
}
}
]
}
test_with_defaults.http.json:
{
"$ref": "config/defaults.json",
"stages": [
{
"name": "test",
"request": {
"url": "{{ base_url }}/test",
"timeout": "{{ timeout }}"
}
}
]
}
Security: Path Traversal Limits¶
The ref_parent_traversal_depth configuration limits how many ../ segments are allowed:
With depth 3, these are valid:
- ../file.json
- ../../file.json
- ../../../file.json
This would fail:
- ../../../../file.json
Circular Reference Detection¶
pytest-httpchain detects and prevents circular references:
a.json:
b.json:
This will raise an error during scenario loading.
Best Practices¶
- Organize by purpose: Group related fragments (auth, common headers, base configs)
- Use meaningful paths:
fragments/auth/login.jsonvsf1.json - Keep references shallow: Deeply nested refs are harder to debug
- Document shared files: Add comments about expected variables
- Version shared fragments: Consider separate directories for breaking changes