[#668] Added 'JsonTrait' for JSON response path and schema assertions.#679
Conversation
WalkthroughThis PR adds a new ChangesJsonTrait feature
Estimated code review effort: 3 (Moderate) | ~30 minutes Suggested labels: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 PHPStan (2.2.2)Composer install failed: the lock file is not up to date with the latest changes in composer.json. Run Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@composer.json`:
- Around line 24-25: The dependency constraint for softcreatr/jsonpath is
incompatible with the project’s declared PHP baseline in composer.json. Update
the package requirement in composer.json by either raising the PHP constraint to
match the minimum version needed by the allowed jsonpath releases or narrowing
the jsonpath version range to a release that still supports PHP 8.2, keeping the
package and PHP requirements consistent.
In `@src/JsonTrait.php`:
- Around line 109-136: The JSON decode-and-check logic is duplicated in
JsonTrait across jsonAssertResponseIsJson, jsonAssertResponseIsNotJson,
jsonPrintLastResponse, and jsonValidateSchema. Extract this into a shared helper
such as jsonDecodeOrFail (or reuse jsonDecode) and update those methods to call
it, keeping the failure message format consistent through the existing
jsonResolveContent and jsonDecode-related flow.
- Around line 337-351: The jsonAssertPathCount() step currently coerces any
non-numeric :count value to 0 via (int) $count, which can hide malformed test
input. Update this method in JsonTrait to validate that $count is numeric before
casting, and throw an ExpectationException with a clear message when it is not;
keep the existing count comparison logic for valid values and preserve the
jsonResolveSingle() / getSession()->getDriver() flow.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: f8dc8205-b595-4131-9dfc-dab14b0650da
📒 Files selected for processing (10)
README.mdSTEPS.mdcomposer.jsonsrc/JsonTrait.phptests/behat/bootstrap/FeatureContext.phptests/behat/features/json.featuretests/behat/fixtures/json_alt.jsontests/behat/fixtures/json_invalid.jsontests/behat/fixtures/json_schema.jsontests/behat/fixtures/json_valid.json
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #679 +/- ##
==========================================
+ Coverage 96.85% 96.95% +0.10%
==========================================
Files 46 47 +1
Lines 3818 3946 +128
==========================================
+ Hits 3698 3826 +128
Misses 120 120 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/JsonTrait.php (1)
598-622: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider extending
jsonDecodeLoose()to also cover the schema decode.The response-content decode now uses
jsonDecodeLoose()(line 605), but the schema decode block (lines 607-610) still duplicates the same decode/json_last_error()/throw pattern inline. Parameterizing the error-message prefix would complete the consolidation this commit already started for the other three call sites.♻️ Proposed refactor
- protected function jsonDecodeLoose(string $content): mixed { + protected function jsonDecodeLoose(string $content, string $error_prefix = 'The response is not valid JSON'): mixed { $data = json_decode($content); if (json_last_error() !== JSON_ERROR_NONE) { - throw new ExpectationException(sprintf('The response is not valid JSON: %s', json_last_error_msg()), $this->getSession()->getDriver()); + throw new ExpectationException(sprintf('%s: %s', $error_prefix, json_last_error_msg()), $this->getSession()->getDriver()); } return $data; }$data = $this->jsonDecodeLoose($this->jsonResolveContent()); - $schema = json_decode($schema_json); - if (json_last_error() !== JSON_ERROR_NONE) { - throw new ExpectationException(sprintf('The provided JSON schema is not valid JSON: %s', json_last_error_msg()), $this->getSession()->getDriver()); - } + $schema = $this->jsonDecodeLoose($schema_json, 'The provided JSON schema is not valid JSON');🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/JsonTrait.php` around lines 598 - 622, The schema decode in jsonValidateSchema still duplicates the loose JSON decode error handling that was consolidated elsewhere. Refactor the schema parsing block to reuse jsonDecodeLoose() for decoding the $schema_json input, and parameterize the error-message prefix so the thrown ExpectationException still clearly identifies schema parsing failures. Keep the existing Validator flow and unique symbols jsonValidateSchema, jsonDecodeLoose, and ExpectationException as the integration points.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/JsonTrait.php`:
- Around line 598-622: The schema decode in jsonValidateSchema still duplicates
the loose JSON decode error handling that was consolidated elsewhere. Refactor
the schema parsing block to reuse jsonDecodeLoose() for decoding the
$schema_json input, and parameterize the error-message prefix so the thrown
ExpectationException still clearly identifies schema parsing failures. Keep the
existing Validator flow and unique symbols jsonValidateSchema, jsonDecodeLoose,
and ExpectationException as the integration points.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: c5d0acee-068e-419b-8bca-651dcd4cfcee
📒 Files selected for processing (2)
src/JsonTrait.phptests/behat/features/json.feature
Closes #668
Summary
Adds
JsonTrait, giving JSON API responses the same assertion treatmentXmlTraitgives XML bodies: valid-JSON checks, JSONPath value/type/existence/count assertions, and JSON Schema validation.Content can come from the live page, a fixture file, or an inline PyString, with fixture/PyString content always taking precedence, mirroring the resolution order already used by
XmlTrait.JSON Schema validation is wired as an optional dependency so consumers who do not need it are not forced to install it.
Changes
src/JsonTrait.phpwith 19 step definitions: two content-fixtureGivensteps (from a file, from a PyString), format-validityThensteps (should be/should not be in JSON format), JSONPath assertions (should exist/not exist/be equal to/not be equal to/contain/not contain/match/not match/be null/be true/be false/have :count element(s)), two JSON SchemaThensteps (inline PyString and from a file), and aWhen I print last JSON responsedebugging step.md5()) so repeated JSONPath queries against the same page do not re-decode on every assertion, and the cache is cleared inBeforeScenario/AfterScenariohooks and whenever fixture content is set.softcreatr/jsonpathtorequireincomposer.jsonfor JSONPath evaluation, andjustinrainbow/json-schematorequire-devplus asuggestentry, since only the two schema-validation steps need it (jsonValidateSchema()guards withclass_exists(Validator::class)).JsonTraitintests/behat/bootstrap/FeatureContext.php.tests/behat/features/json.featurecovering every step in both the passing and failing direction, plus a scenario asserting that decoded JSON is correctly reloaded when navigating between different JSON responses.tests/behat/fixtures/json_valid.json,json_invalid.json,json_alt.json, andjson_schema.json.STEPS.mdand added theJsonTraitrow to the trait table inREADME.md.Screenshots
N/A
Before / After
Summary
JsonTraitfor JSON response assertions with valid-JSON checks, JSONPath-based value/type/existence/count assertions, JSON Schema validation, and a debug step to print the last JSON response.FeatureContextand added Behat coverage plus JSON/schema fixtures for positive and negative scenarios.STEPS.mdandREADME.md, and added the required JSONPath / JSON Schema Composer dependencies.Notes
CONTRIBUTING.mdfor the new Behat step definitions: assertion methods are prefixed with the trait name andAssert,Given/When/Thenstrings use the expected wording (I <verb>forWhen,should/should notforThen), and no step definitions rely on regex patterns—no Critical violations found.