Summary
Add JSON Schema validation to AssertableJson via matches_schema(), with a thin assert_json_schema() wrapper on the response mixin. Validates entire JSON response bodies against a JSON Schema. Scoped validation comes for free through existing AssertableJson architecture.
Motivation
assert_json_structure checks keys and types using a custom format. JSON Schema is the industry standard for API contract validation — it handles nested objects, required fields, oneOf/anyOf, $ref, format constraints, etc. This bridges the gap between ad-hoc assertions and formal contract testing.
API
# inline dict
response.assert_json_schema({"type": "object", "properties": {"id": {"type": "integer"}}})
# local file
response.assert_json_schema("schemas/user.json")
response.assert_json_schema(Path("schemas/user.json"))
# remote URL
response.assert_json_schema("https://api.example.com/schemas/user.json")
# scoped — works automatically via AssertableJson
response.assert_json("data.user", lambda user: (
user.matches_schema(user_schema)
))
All return Self for chaining.
Schema sources
| Source |
Type |
Behavior |
| Inline dict |
dict |
Used directly |
| Local file |
str or Path ending in .json |
Read and json.load |
| URL |
str starting with http:// or https:// |
Fetch via urllib.request and parse |
Architecture
Follows the existing pattern where every assert_json_* method on the mixin is a thin wrapper that delegates to AssertableJson:
| Layer |
Method |
Responsibility |
AssertableJson |
matches_schema(dict | str | Path) |
Core: resolve schema, validate self._data |
JsonContentAssertionsMixin |
assert_json_schema(dict | str | Path) |
Thin wrapper: self.assert_json().matches_schema(schema) |
Scoped validation comes for free because matches_schema operates on self._data, which is already scoped by AssertableJson's existing json() method. No new mixin or file needed.
A private _resolve_schema helper in json.py handles schema source resolution (dict passthrough, file read, URL fetch). URL fetching uses urllib.request from stdlib.
Design decisions
- Core logic on
AssertableJson — consistent with every other assertion in the module (where, has, count, structure, etc.)
- Hard dependency —
jsonschema>=4.0 added to project.dependencies
- URL support via stdlib —
urllib.request, no extra HTTP client dependency
- Error reporting — surface
jsonschema.ValidationError.message and path in the AssertionError
- No schema caching — caller's responsibility
- No custom validators or format checkers — plain
jsonschema.validate
File changes
| File |
Change |
src/pyssertive/http/json.py |
Add matches_schema() to AssertableJson, add assert_json_schema() to JsonContentAssertionsMixin, add _resolve_schema helper |
pyproject.toml |
Add jsonschema>=4.0 to dependencies |
tests/http/test_contract.py |
New — tests using local .json schema files |
tests/schemas/ |
New — test schema fixture files |
Summary
Add JSON Schema validation to
AssertableJsonviamatches_schema(), with a thinassert_json_schema()wrapper on the response mixin. Validates entire JSON response bodies against a JSON Schema. Scoped validation comes for free through existingAssertableJsonarchitecture.Motivation
assert_json_structurechecks keys and types using a custom format. JSON Schema is the industry standard for API contract validation — it handles nested objects,requiredfields,oneOf/anyOf,$ref, format constraints, etc. This bridges the gap between ad-hoc assertions and formal contract testing.API
All return
Selffor chaining.Schema sources
dictstrorPathending in.jsonjson.loadstrstarting withhttp://orhttps://urllib.requestand parseArchitecture
Follows the existing pattern where every
assert_json_*method on the mixin is a thin wrapper that delegates toAssertableJson:AssertableJsonmatches_schema(dict | str | Path)self._dataJsonContentAssertionsMixinassert_json_schema(dict | str | Path)self.assert_json().matches_schema(schema)Scoped validation comes for free because
matches_schemaoperates onself._data, which is already scoped byAssertableJson's existingjson()method. No new mixin or file needed.A private
_resolve_schemahelper injson.pyhandles schema source resolution (dict passthrough, file read, URL fetch). URL fetching usesurllib.requestfrom stdlib.Design decisions
AssertableJson— consistent with every other assertion in the module (where,has,count,structure, etc.)jsonschema>=4.0added toproject.dependenciesurllib.request, no extra HTTP client dependencyjsonschema.ValidationError.messageandpathin theAssertionErrorjsonschema.validateFile changes
src/pyssertive/http/json.pymatches_schema()toAssertableJson, addassert_json_schema()toJsonContentAssertionsMixin, add_resolve_schemahelperpyproject.tomljsonschema>=4.0to dependenciestests/http/test_contract.py.jsonschema filestests/schemas/