├── LICENSE.txt ├── composer.json └── src ├── Constraint ├── JsonValueMatches.php ├── JsonValueMatchesMany.php └── JsonValueMatchesSchema.php ├── Functions.php └── JsonAssertions.php /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Martin Helmich 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helmich/phpunit-json-assert", 3 | "description": "PHPUnit assertions for JSON documents", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Martin Helmich", 8 | "email": "m.helmich@mittwald.de" 9 | } 10 | ], 11 | "require": { 12 | "php": "^8.1", 13 | "softcreatr/jsonpath": "^0.8", 14 | "justinrainbow/json-schema": "^5.0" 15 | }, 16 | "conflict": { 17 | "phpunit/phpunit": "<8.0 || >= 13.0" 18 | }, 19 | "require-dev": { 20 | "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "Helmich\\JsonAssert\\": "src/" 25 | } 26 | }, 27 | "autoload-dev": { 28 | "files": [ 29 | "src/Functions.php" 30 | ] 31 | }, 32 | "config": { 33 | "sort-packages": true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Constraint/JsonValueMatches.php: -------------------------------------------------------------------------------- 1 | jsonPath = $jsonPath; 45 | $this->constraint = $constraint; 46 | $this->matchAll = $matchAll; 47 | } 48 | 49 | /** 50 | * Returns a string representation of the object. 51 | * 52 | * @return string 53 | */ 54 | public function toString(): string 55 | { 56 | return "matches " . $this->constraint->toString() . " at JSON path '{$this->jsonPath}'"; 57 | } 58 | 59 | /** 60 | * @inheritdoc 61 | */ 62 | protected function matches($other): bool 63 | { 64 | if (is_string($other)) { 65 | $other = json_decode($other, true); 66 | } 67 | 68 | $result = (new JSONPath($other))->find($this->jsonPath); 69 | if (!isset($result[0])) { 70 | return false; 71 | } 72 | 73 | $combineFunc = $this->buildCombinationFunction(); 74 | $matches = null; 75 | 76 | foreach ($result as $v) { 77 | if ($v instanceof JSONPath) { 78 | $v = $v->getData(); 79 | } 80 | 81 | $singleMatchResult = $this->constraint->evaluate($v, '', true); 82 | $matches = $combineFunc($matches, $singleMatchResult); 83 | } 84 | 85 | return $matches; 86 | } 87 | 88 | /** 89 | * @return callable 90 | */ 91 | protected function buildCombinationFunction(): callable 92 | { 93 | if ($this->matchAll) { 94 | return function ($first, $second) { 95 | return ($first === null) ? $second : $first && $second; 96 | }; 97 | } 98 | 99 | return function ($first, $second) { 100 | return ($first === null) ? $second : $first || $second; 101 | }; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Constraint/JsonValueMatchesMany.php: -------------------------------------------------------------------------------- 1 | $constraint) { 31 | if (!$constraint instanceof Constraint) { 32 | $constraint = new IsEqual($constraint); 33 | } 34 | 35 | $this->constraints[] = new JsonValueMatches($key, $constraint); 36 | } 37 | } 38 | 39 | /** 40 | * Returns a string representation of the object. 41 | * 42 | * @return string 43 | */ 44 | public function toString(): string 45 | { 46 | return implode( 47 | ' and ', 48 | array_map( 49 | function (Constraint $constraint) { 50 | return $constraint->toString(); 51 | }, 52 | $this->constraints 53 | ) 54 | ); 55 | } 56 | 57 | /** 58 | * @inheritdoc 59 | */ 60 | protected function matches($other): bool 61 | { 62 | foreach ($this->constraints as $constraint) { 63 | if (!$constraint->evaluate($other, '', true)) { 64 | return false; 65 | } 66 | } 67 | return true; 68 | } 69 | 70 | /** 71 | * Returns a string representation of matches that evaluate to false. 72 | * 73 | * @return string 74 | */ 75 | protected function additionalFailureDescription($other): string 76 | { 77 | /** @var string[] */ 78 | $failedConstraints = array(); 79 | 80 | foreach ($this->constraints as $constraint) { 81 | if (!$constraint->evaluate($other, '', true)) { 82 | $failedConstraints[] = $constraint->toString(); 83 | } 84 | } 85 | 86 | return "\n" . implode("\n", $failedConstraints); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Constraint/JsonValueMatchesSchema.php: -------------------------------------------------------------------------------- 1 | schema = $this->forceToObject($schema); 29 | } 30 | 31 | /** 32 | * VERY dirty hack to force a JSON document into an object. 33 | * 34 | * Yell if you can think of something better. 35 | * 36 | * @param array|stdClass $jsonDocument 37 | * @return stdClass 38 | */ 39 | private function forceToObject($jsonDocument) 40 | { 41 | if (is_string($jsonDocument)) { 42 | return json_decode($jsonDocument); 43 | } 44 | 45 | return json_decode(json_encode($jsonDocument)); 46 | } 47 | 48 | /** 49 | * @inheritdoc 50 | */ 51 | protected function matches($other): bool 52 | { 53 | $other = $this->forceToObject($other); 54 | 55 | $validator = new Validator(); 56 | $validator->check($other, $this->schema); 57 | 58 | return $validator->isValid(); 59 | } 60 | 61 | /** 62 | * @inheritdoc 63 | */ 64 | protected function additionalFailureDescription($other): string 65 | { 66 | $other = $this->forceToObject($other); 67 | 68 | $validator = new Validator(); 69 | $validator->check($other, $this->schema); 70 | 71 | return implode("\n", array_map(function ($error) { 72 | return sprintf("[%s] %s", $error['property'], $error['message']); 73 | }, $validator->getErrors())); 74 | } 75 | 76 | /** 77 | * Returns a string representation of the object. 78 | * 79 | * @return string 80 | */ 81 | public function toString(): string 82 | { 83 | return 'matches JSON schema'; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Functions.php: -------------------------------------------------------------------------------- 1 |