JSON Path Assertions
For testing nested attributes or specific fields inside a JSON response body, you can use the [assert.json] subtable. This provides granular assertion rules on individual JSON nodes.
1. Syntax & Configuration
Rumour supports two equivalent TOML syntaxes for declaring JSON path assertions:
Option A: Dotted Subtable Syntax (Recommended for multi-constraint assertions)
Define a separate subtable for each target path:
[request]
method = "POST"
url = "{{base_url}}/post"
[assert.json."json.id"]
equal = 123
[assert.json."json.profile.email"]
equal = "jane@example.com"
regex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
Option B: Inline Table Syntax (Sleeker for single-constraint assertions)
Group all path assertions under a single [assert.json] block using inline TOML tables:
[request]
method = "POST"
url = "{{base_url}}/post"
[assert.json]
"json.id" = { equal = 123 }
"json.profile.email" = { equal = "jane@example.com", regex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" }
2. Operators Reference
Each JSON path block supports the following evaluation operators:
| Operator | Type | Description |
|---|---|---|
equal | JSON Value | Asserts that the field is exactly equal to the value (supports strings, numbers, booleans, and null). |
not_equal | JSON Value | Asserts that the field is not equal to the specified value. |
contains | String | Asserts that the targeted field contains the specified substring. ⚠️ Warning: The contains operator is strictly supported for String nodes. If used on an array, the assertion will fail. |
exists | Boolean | Checks if the JSON path exists (true) or is absent (false) from the payload. |
greater_than | Float / Int | Checks if the target numeric node is strictly greater than this value (alias greaterthan). |
less_than | Float / Int | Checks if the target numeric node is strictly less than this value (alias lessthan). |
regex | String | Asserts that the string value matches the regular expression pattern. |
3. Nested Path Traversal & Array Indexing
Rumour traverses JSON using dot notation.
A. Property Navigation
Access object children by separating keys with a dot:
{
"company": {
"name": "Acme Corp"
}
}
- Path:
"company.name"(evaluates to"Acme Corp")
B. Array Indexing Syntax
Rumour supports two syntaxes for array index traversal:
- Standard Dot Navigation: Treat the array index as a segment.
- Path:
"company.departments.0.name"
- Path:
- Bracket Navigation: Enclose the index in brackets.
- Path:
"company.departments.[0].name"
- Path:
Rumour automatically strips outer brackets [ and ] from any path segment before parsing it as a numerical array index.
4. JSON Assertion Examples (httpbin.org)
Here are complete request definitions asserting conditions against httpbin.org.
Crucial Concept: The [assert.json] block ALWAYS evaluates the RESPONSE body returned by the server, never the request you are sending!
In the examples below, we are sending a POST request to https://httpbin.org/post. This specific testing server echoes the JSON we send it back to us, nested inside a root key called "json". That is why our assertion paths start with "json." (e.g., "json.name" instead of just "name"). We are asserting on the server's response.
A. Dotted Subtable Syntax Example
# test_operators.toml
[request]
method = "POST"
url = "{{base_url}}/post"
[headers]
User-Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
[body]
type = "json"
raw = '{"name": "Alice", "age": 30, "roles": ["admin", "editor"], "active": true, "nested": {"id": 123}}'
[assert.json."json.name"]
equal = "Alice"
not_equal = "Bob"
regex = "^Ali"
contains = "lic"
[assert.json."json.age"]
greater_than = 25.0
less_than = 35.0
[assert.json."json.roles.0"]
equal = "admin"
[assert.json."json.roles.[1]"]
equal = "editor"
[assert.json."json.active"]
equal = true
[assert.json."json.nested.id"]
exists = true
[assert.json."json.non_existent"]
exists = false
Successful Execution Output Trace
POST https://httpbin.org/post
URL: https://httpbin.org/post
✓ SUCCESS: /home/bugsfounder/workspace/testing/json_assert_example/users/requests/test_operators.toml (2363ms)
✓ /home/bugsfounder/workspace/testing/json_assert_example/users/requests/test_operators.toml → PASS (1 Pass, 0 Fail, 0 Skip)
╭──────────────────────────────────────────────────────────────────────────╮
│ RUMOUR EXECUTION REPORT │
├──────────────────────────────────────────────────────────────────────────┤
│ Total Requests: 1 │
│ Successful: 1 │
│ Failed: 0 │
│ Skipped: 0 │
│ Success Rate: 100.0% │
│ Total Time: 2367ms │
╰──────────────────────────────────────────────────────────────────────────╯
✓ Successful Requests:
- /home/bugsfounder/workspace/testing/json_assert_example/users/requests/test_operators.toml [200] [2367ms]
Actionable Recommendations:
→ Run with --json to export this report for your CI/CD pipeline.
Let's break down what just happened:
- The Live Action: Rumour executes the
POSTrequest and successfully evaluates all of our complex JSON path constraints in real-time. Because every single assertion passed perfectly, it gets a clean✓ SUCCESS. - The Final Report: The execution report summarizes the results. You get the peace of mind knowing that the complex, deeply nested JSON data is shaped exactly the way you expect it to be!
B. Inline Table Syntax Example
# test_operators_inline.toml
[request]
method = "POST"
url = "{{base_url}}/post"
[headers]
User-Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
[body]
type = "json"
raw = '{"name": "Alice", "age": 30, "roles": ["admin", "editor"], "active": true, "nested": {"id": 123}}'
[assert.json]
"json.name" = { equal = "Alice", not_equal = "Bob", regex = "^Ali", contains = "lic" }
"json.age" = { greater_than = 25.0, less_than = 35.0 }
"json.roles.0" = { equal = "admin" }
"json.roles.[1]" = { equal = "editor" }
"json.active" = { equal = true }
"json.nested.id" = { exists = true }
"json.non_existent" = { exists = false }
Successful Execution Output Trace
POST https://httpbin.org/post
URL: https://httpbin.org/post
✓ SUCCESS: /home/bugsfounder/workspace/testing/json_assert_example/users/requests/test_operators_inline.toml (1854ms)
✓ /home/bugsfounder/workspace/testing/json_assert_example/users/requests/test_operators_inline.toml → PASS (1 Pass, 0 Fail, 0 Skip)
╭──────────────────────────────────────────────────────────────────────────╮
│ RUMOUR EXECUTION REPORT │
├──────────────────────────────────────────────────────────────────────────┤
│ Total Requests: 1 │
│ Successful: 1 │
│ Failed: 0 │
│ Skipped: 0 │
│ Success Rate: 100.0% │
│ Total Time: 1856ms │
╰──────────────────────────────────────────────────────────────────────────╯
✓ Successful Requests:
- /home/bugsfounder/workspace/testing/json_assert_example/users/requests/test_operators_inline.toml [200] [1856ms]
Actionable Recommendations:
→ Run with --json to export this report for your CI/CD pipeline.
Let's break down what just happened:
Just like the dotted syntax example above, Rumour completely evaluated the inline assertions and generated an identical success report. The inline syntax is simply a cleaner way to write your .toml files—under the hood, Rumour handles them exactly the same way!
5. Failing JSON Assertions & Diagnostics
If one or more JSON path assertions fail, Rumour halts execution and outputs exact details of the mismatches.
Failing Example TOML
[assert.json."json.age"]
greater_than = 35.0 # actual is 30
[assert.json."json.roles"]
contains = "admin" # FAILS: contains is not supported on arrays
Diagnostics output:
✗ FAILED: test_operators.toml (931ms) - JSON Path: json.age: Expected > 35, got 30 | JSON Path: json.roles: Value is not a string or not found (expected containment)
Let's break down what just happened:
- The Instant Failure: One or more of our assertions failed, so Rumour immediately halts execution and throws an
✗ FAILEDerror. - The Detailed Post-Mortem: Look closely at the error message. Rumour doesn't just say "failed"—it tells you exactly which JSON paths broke and why, separated by a pipe (
|).- For
json.age, it explicitly statesExpected > 35, got 30. - For
json.roles, it warns you thatcontainsis only meant for strings, but it found an array instead.
- For
This kind of pinpoint accuracy saves you hours of digging through JSON payloads manually!