├── .github ├── FUNDING.yml ├── dependabot.yml ├── stale.yml └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .releaserc ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml.dist ├── src ├── Assert.php ├── AssertClass.php └── Extension │ ├── Symfony.php │ └── SymfonyClass.php └── tests ├── AssertClassTest.php ├── AssertTraitImpl.php ├── AssertTraitTest.php ├── Extension └── SymfonyTest.php ├── Utils.php ├── json ├── assertJsonMatchesSchema_simple.json ├── simple.json └── testAssertJsonValueEquals.json └── schemas ├── assertJsonMatchesSchema_simple.schema.json ├── definitions.schema.json ├── error.schema.json └── test.schema.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [estahn] 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | 8 | - package-ecosystem: "composer" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: [pull_request] 3 | 4 | jobs: 5 | psalm: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | 10 | - name: Psalm 11 | uses: docker://vimeo/psalm-github-actions 12 | with: 13 | security_analysis: true 14 | report_file: results.sarif 15 | 16 | - name: Upload Security Analysis results to GitHub 17 | uses: github/codeql-action/upload-sarif@v1 18 | with: 19 | sarif_file: results.sarif 20 | 21 | # codecov: 22 | # runs-on: ubuntu-latest 23 | # steps: 24 | # - uses: actions/checkout@v2 25 | # with: 26 | # fetch-depth: 0 27 | 28 | # - name: Cache Composer dependencies 29 | # uses: actions/cache@v2 30 | # with: 31 | # path: /tmp/composer-cache 32 | # key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }} 33 | 34 | # - uses: php-actions/composer@v6 35 | 36 | # - uses: php-actions/phpunit@v2 37 | # env: 38 | # XDEBUG_MODE: coverage 39 | # with: 40 | # php_extensions: xdebug 41 | # args: --coverage-clover coverage.xml 42 | 43 | # - uses: codecov/codecov-action@v1 44 | # with: 45 | # file: ./coverage.xml 46 | # fail_ci_if_error: true 47 | # verbose: true 48 | 49 | test: 50 | runs-on: ubuntu-latest 51 | strategy: 52 | matrix: 53 | php: 54 | - 7.4 55 | - 8.0 56 | include: 57 | - php: 7.4 58 | phpunit: 9.5.0 59 | - php: 8.0 60 | phpunit: 9.5.0 61 | 62 | steps: 63 | - uses: actions/checkout@v2 64 | 65 | - name: Cache Composer dependencies 66 | uses: actions/cache@v2 67 | with: 68 | path: /tmp/composer-cache 69 | key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }} 70 | 71 | - uses: php-actions/composer@v6 72 | with: 73 | php_version: ${{ matrix.php }} 74 | 75 | - uses: php-actions/phpunit@v9 76 | with: 77 | php_version: ${{ matrix.php }} 78 | version: ${{ matrix.phpunit }} 79 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: [workflow_dispatch] 3 | jobs: 4 | release: 5 | name: Release 6 | runs-on: ubuntu-latest 7 | # if: github.ref == 'refs/heads/master' 8 | steps: 9 | 10 | - name: Setup Node.js for use with actions 11 | uses: actions/setup-node@v2.1.5 12 | 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | 16 | - name: Unshallow 17 | run: git fetch --prune --unshallow 18 | 19 | - name: Run semantic-release 20 | run: npx -p @semantic-release/changelog -p semantic-release@17.3.0 -p @semantic-release/git semantic-release 21 | env: 22 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | tests/log/ 3 | composer.lock 4 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | --- 2 | repositoryUrl: https://github.com/estahn/phpunit-json-assertions 3 | verifyConditions: ['@semantic-release/github'] 4 | prepare: [] 5 | publish: ['@semantic-release/github'] 6 | success: ['@semantic-release/github'] 7 | fail: ['@semantic-release/github'] 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Enrico Stahn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # phpunit-json-assertions 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/estahn/phpunit-json-assertions/version.png)](https://packagist.org/packages/estahn/phpunit-json-assertions) 4 | [![Total Downloads](https://poser.pugx.org/estahn/phpunit-json-assertions/d/total.png)](https://packagist.org/packages/estahn/phpunit-json-assertions) 5 | [![Test](https://github.com/estahn/phpunit-json-assertions/actions/workflows/build.yml/badge.svg)](https://github.com/estahn/phpunit-json-assertions/actions/workflows/build.yml) 6 | [![StyleCI](https://styleci.io/repos/53177096/shield)](https://styleci.io/repos/53177096) 7 | [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/estahn/phpunit-json-assertions.svg)](http://isitmaintained.com/project/estahn/phpunit-json-assertions "Average time to resolve an issue") 8 | [![Percentage of issues still open](http://isitmaintained.com/badge/open/estahn/phpunit-json-assertions.svg)](http://isitmaintained.com/project/estahn/phpunit-json-assertions "Percentage of issues still open") 9 | 10 | JSON assertions for PHPUnit includes traits/methods to help validate your JSON data through various methods. 11 | 12 | ## Features 13 | 14 | * Validate your JSON data via JSON Schema 15 | * describes your existing data format 16 | * clear, human- and machine-readable documentation 17 | * complete structural validation, useful for 18 | * automated testing 19 | * validating client-submitted data 20 | * See more details [here](http://json-schema.org/) 21 | * Access JSON data through expressions (e.g. `foo.bar[3]`) 22 | * See more details [here](http://jmespath.org/examples.html) 23 | 24 | ## Install 25 | 26 | $ composer require estahn/phpunit-json-assertions --dev 27 | 28 | or in your `composer.json`: 29 | 30 | ```json 31 | { 32 | "require-dev": { 33 | "estahn/phpunit-json-assertions": "@stable" 34 | } 35 | } 36 | ``` 37 | 38 | ## Asserts 39 | 40 | | Assert | Description | Available in | 41 | | ----------------------------- | ---------------------------------------------------------------------------- | ------------ | 42 | | [assertJsonMatchesSchema](https://github.com/estahn/phpunit-json-assertions/wiki/assertJsonMatchesSchema) | Asserts that json content is valid according to the provided schema file | All | 43 | | assertJsonMatchesSchemaString | Asserts that json content is valid according to the provided schema string | All | 44 | | assertJsonValueEquals | Asserts if the value retrieved with the expression equals the expected value | All | 45 | | assertJsonValueEquals | Asserts if the value retrieved with the expression equals the expected value | All | 46 | | assertJsonResponse | Asserts that a response is successful and of type json | Symfony | 47 | 48 | ## Usage 49 | 50 | You can either use the `trait` or `class` version. 51 | 52 | ### Trait 53 | 54 | ```php 55 | assertJsonMatchesSchema($json, './my-schema.json'); 82 | $this->assertJsonValueEquals(1, '* | [0]', $json); 83 | } 84 | } 85 | ``` 86 | 87 | ### Class 88 | 89 | In case you don't want to use the `trait` you can use the provided class wich extends from `\PHPUnit_Framework_TestCase`. 90 | You can either extend your test case or use the static methods like below. 91 | 92 | ```php 93 | addSchema('https://iglu.foobar.com/myschema.json', (object)['type' => 'string']); 136 | ``` 137 | 138 | With this in place the resolver will take the schema that is already in place without downloading it again. 139 | 140 | ```php 141 | addSchema('', obj); 154 | ... 155 | } 156 | 157 | public function testJsonDocumentIsValid() 158 | { 159 | // my-schema.json 160 | // 161 | // { 162 | // "type" : "object", 163 | // "properties" : { 164 | // "foo" : { 165 | // "type" : "integer" 166 | // } 167 | // }, 168 | // "required" : [ "foo" ] 169 | // } 170 | 171 | $json = json_decode('{"foo":1}'); 172 | 173 | JsonAssert::assertJsonMatchesSchema($json, './my-schema.json'); 174 | JsonAssert::assertJsonValueEquals(1, '* | [0]', $json); 175 | } 176 | } 177 | ``` 178 | 179 | ## Extensions 180 | 181 | `phpunit-json-assertions` provides extensions for simpler handling in different use cases. 182 | 183 | ### Symfony HttpFoundation Component 184 | 185 | The extension `EnricoStahn\JsonAssert\Extension\Symfony` allows to pass in the actual response object generated 186 | by the symfony framework and takes care of the decoding part. 187 | 188 | BEFORE: 189 | ```php 190 | use EnricoStahn\JsonAssert\Assert as JsonAssert; 191 | 192 | // ... 193 | 194 | $content = $response->getContent(); 195 | $json = json_decode($content); 196 | JsonAssert::assertJsonMatchesSchemaString('./my-schema.json', $json); 197 | ``` 198 | 199 | AFTER: 200 | ```php 201 | use EnricoStahn\JsonAssert\Extension\Symfony as JsonAssert; 202 | 203 | // ... 204 | 205 | JsonAssert::assertJsonMatchesSchemaString('./my-schema.json', $response); 206 | ``` 207 | 208 | ## Tests 209 | 210 | To run the test suite, you need [composer](http://getcomposer.org). 211 | 212 | $ composer install 213 | $ bin/phpunit 214 | 215 | ## Badge Mania 216 | [![Build Status](https://scrutinizer-ci.com/g/estahn/phpunit-json-assertions/badges/build.png?b=master)](https://scrutinizer-ci.com/g/estahn/phpunit-json-assertions/build-status/master) 217 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/estahn/phpunit-json-assertions/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/estahn/phpunit-json-assertions/?branch=master) 218 | [![Code Coverage](https://scrutinizer-ci.com/g/estahn/phpunit-json-assertions/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/estahn/phpunit-json-assertions/?branch=master) 219 | [![Codacy Badge](https://api.codacy.com/project/badge/grade/0bbc8fdeb4044287bbce009adc07ca39)](https://www.codacy.com/app/estahn/phpunit-json-assertions) 220 | [![Codacy Badge](https://api.codacy.com/project/badge/coverage/0bbc8fdeb4044287bbce009adc07ca39)](https://www.codacy.com/app/estahn/phpunit-json-assertions) 221 | 222 | ## Alternatives 223 | * https://github.com/martin-helmich/phpunit-json-assert - Doesn't support JSON Schema and uses [JSONPath](https://github.com/FlowCommunications/JSONPath) instead of [jmespath.php](https://github.com/jmespath/jmespath.php) 224 | 225 | ## License 226 | 227 | The phpunit-json-assertions library is licensed under the [MIT](LICENSE). 228 | 229 | 230 | ## Stargazers over time 231 | 232 | [![Stargazers over time](https://starchart.cc/estahn/phpunit-json-assertions.svg)](https://starchart.cc/estahn/phpunit-json-assertions) 233 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "estahn/phpunit-json-assertions", 3 | "type": "library", 4 | "description": "JSON assertions for PHPUnit (including JSON Schema)", 5 | "keywords": ["json", "phpunit", "schema"], 6 | "homepage": "https://github.com/estahn/phpunit-json-assertions", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Enrico Stahn", 11 | "email": "enrico.stahn@gmail.com" 12 | } 13 | ], 14 | "minimum-stability": "stable", 15 | "require": { 16 | "php": "^7.4|^8.0", 17 | "justinrainbow/json-schema": "^5.0", 18 | "mtdowling/jmespath.php": "^2.3", 19 | "ext-json": "*" 20 | }, 21 | "require-dev": { 22 | "phpunit/phpunit": "^9", 23 | "codacy/coverage": "dev-master", 24 | "symfony/http-foundation": "^2.8|^3.0|^5.0" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "EnricoStahn\\JsonAssert\\": "src/" 29 | } 30 | }, 31 | "autoload-dev": { 32 | "psr-4": { 33 | "EnricoStahn\\JsonAssert\\Tests\\": "tests/" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | tests/ 11 | 12 | 13 | 14 | 15 | 16 | src/ 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Assert.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace EnricoStahn\JsonAssert; 13 | 14 | use JmesPath\Env; 15 | use JsonSchema\Constraints\Factory; 16 | use JsonSchema\SchemaStorage; 17 | use JsonSchema\Validator; 18 | use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; 19 | 20 | /** 21 | * Asserts to validate JSON data. 22 | * 23 | * - All assert methods expect deserialised JSON data (an actual object or array) 24 | * since the deserialisation method should be up to the user. 25 | * - We provide a convenience method to transfer whatever into a JSON object (see ::getJsonObject(mixed)) 26 | */ 27 | trait Assert 28 | { 29 | /** 30 | * @var ?SchemaStorage 31 | */ 32 | private static ?SchemaStorage $schemaStorage = null; 33 | 34 | /** 35 | * Asserts that json content is valid according to the provided schema file. 36 | * 37 | * Example: 38 | * 39 | * static::assertJsonMatchesSchema(json_decode('{"foo":1}'), './schema.json') 40 | * 41 | * @param array|object $content JSON array or object 42 | * @param ?string $schema Path to the schema file 43 | */ 44 | public static function assertJsonMatchesSchema($content, ?string $schema = null): void 45 | { 46 | if (self::$schemaStorage === null) { 47 | self::$schemaStorage = new SchemaStorage(); 48 | } 49 | 50 | if ($schema !== null && !file_exists($schema)) { 51 | throw new FileNotFoundException($schema); 52 | } 53 | 54 | $schemaObject = null; 55 | 56 | if ($schema !== null) { 57 | $schemaObject = json_decode(file_get_contents($schema)); 58 | self::$schemaStorage->addSchema('file://'.$schema, $schemaObject); 59 | } 60 | 61 | $validator = new Validator(new Factory(self::$schemaStorage)); 62 | $validator->validate($content, $schemaObject); 63 | 64 | $message = '- Property: %s, Constraint: %s, Message: %s'; 65 | $messages = array_map(function ($exception) use ($message) { 66 | return sprintf($message, $exception['property'], $exception['constraint'], $exception['message']); 67 | }, $validator->getErrors()); 68 | $messages[] = '- Response: '.json_encode($content); 69 | 70 | \PHPUnit\Framework\Assert::assertTrue($validator->isValid(), implode("\n", $messages)); 71 | } 72 | 73 | /** 74 | * Asserts that json content is valid according to the provided schema string. 75 | * 76 | * @param string $schema Schema data 77 | * @param array|object $content JSON content 78 | */ 79 | public static function assertJsonMatchesSchemaString(string $schema, $content): void 80 | { 81 | $file = tempnam(sys_get_temp_dir(), 'json-schema-'); 82 | file_put_contents($file, $schema); 83 | 84 | self::assertJsonMatchesSchema($content, $file); 85 | } 86 | 87 | /** 88 | * Asserts if the value retrieved with the expression equals the expected value. 89 | * 90 | * Example: 91 | * 92 | * static::assertJsonValueEquals(33, 'foo.bar[0]', $json); 93 | * 94 | * @param mixed $expected Expected value 95 | * @param string $expression Expression to retrieve the result 96 | * (e.g. locations[?state == 'WA'].name | sort(@)) 97 | * @param array|object|string $json JSON Content 98 | */ 99 | public static function assertJsonValueEquals($expected, string $expression, $json): void 100 | { 101 | $result = Env::search($expression, $json); 102 | 103 | \PHPUnit\Framework\Assert::assertEquals($expected, $result); 104 | \PHPUnit\Framework\Assert::assertEquals(gettype($expected), gettype($result)); 105 | } 106 | 107 | /** 108 | * Helper method to deserialise a JSON string into an object. 109 | * 110 | * @param mixed $data The JSON string 111 | * 112 | * @return array|object 113 | */ 114 | public static function getJsonObject($data) 115 | { 116 | return (is_array($data) || is_object($data)) ? $data : json_decode($data); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/AssertClass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace EnricoStahn\JsonAssert; 13 | 14 | use PHPUnit\Framework\TestCase; 15 | 16 | class AssertClass extends TestCase 17 | { 18 | use Assert; 19 | } 20 | -------------------------------------------------------------------------------- /src/Extension/Symfony.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace EnricoStahn\JsonAssert\Extension; 13 | 14 | use EnricoStahn\JsonAssert\Assert; 15 | use Symfony\Component\HttpFoundation\Response; 16 | 17 | trait Symfony 18 | { 19 | /** 20 | * Asserts that json content is valid according to the provided schema file. 21 | * 22 | * Example: 23 | * 24 | * static::assertJsonMatchesSchema(json_decode('{"foo":1}'), './schema.json') 25 | * 26 | * @param string $schema Path to the schema file 27 | * @param Response $response JSON array or object 28 | */ 29 | public static function assertJsonMatchesSchema(string $schema, Response $response): void 30 | { 31 | Assert::assertJsonMatchesSchema(json_decode($response->getContent()), $schema); 32 | } 33 | 34 | /** 35 | * Asserts that json content is valid according to the provided schema string. 36 | * 37 | * @param string $schema Schema data 38 | * @param Response $response JSON content 39 | */ 40 | public static function assertJsonMatchesSchemaString(string $schema, Response $response): void 41 | { 42 | Assert::assertJsonMatchesSchemaString($schema, json_decode($response->getContent())); 43 | } 44 | 45 | /** 46 | * Asserts if the value retrieved with the expression equals the expected value. 47 | * 48 | * Example: 49 | * 50 | * static::assertJsonValueEquals(33, 'foo.bar[0]', $json); 51 | * 52 | * @param mixed $expected Expected value 53 | * @param string $expression Expression to retrieve the result 54 | * (e.g. locations[?state == 'WA'].name | sort(@)) 55 | * @param Response $response JSON Content 56 | */ 57 | public static function assertJsonValueEquals($expected, string $expression, Response $response): void 58 | { 59 | Assert::assertJsonValueEquals($expected, $expression, json_decode($response->getContent())); 60 | } 61 | 62 | /** 63 | * Asserts that a response is successful and of type json. 64 | * 65 | * @param Response $response Response object 66 | * @param int $statusCode Expected status code (default 200) 67 | * 68 | * @see \Bazinga\Bundle\RestExtraBundle\Test\WebTestCase::assertJsonResponse() 69 | */ 70 | public static function assertJsonResponse(Response $response, int $statusCode = 200): void 71 | { 72 | \PHPUnit\Framework\Assert::assertEquals( 73 | $statusCode, 74 | $response->getStatusCode(), 75 | $response->getContent() 76 | ); 77 | \PHPUnit\Framework\Assert::assertTrue( 78 | $response->headers->contains('Content-Type', 'application/json'), 79 | $response->headers 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Extension/SymfonyClass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace EnricoStahn\JsonAssert\Extension; 13 | 14 | use PHPUnit\Framework\TestCase; 15 | 16 | class SymfonyClass extends TestCase 17 | { 18 | use Symfony; 19 | } 20 | -------------------------------------------------------------------------------- /tests/AssertClassTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace EnricoStahn\JsonAssert\Tests; 13 | 14 | use EnricoStahn\JsonAssert\AssertClass; 15 | use PHPUnit\Framework\TestCase; 16 | 17 | class AssertClassTest extends TestCase 18 | { 19 | public function testClassInstance() 20 | { 21 | static::assertInstanceOf('EnricoStahn\JsonAssert\AssertClass', new AssertClass()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/AssertTraitImpl.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace EnricoStahn\JsonAssert\Tests; 13 | 14 | use EnricoStahn\JsonAssert\Assert as JsonAssert; 15 | use JsonSchema\SchemaStorage; 16 | use PHPUnit\Framework\TestCase; 17 | 18 | class AssertTraitImpl extends TestCase 19 | { 20 | use JsonAssert; 21 | 22 | public function setUp(): void 23 | { 24 | self::$schemaStorage = new SchemaStorage(); 25 | } 26 | 27 | /** 28 | * @param string $id 29 | * @param array|object $schema 30 | * 31 | * @return SchemaStorage 32 | */ 33 | public function testWithSchemaStore(string $id, $schema): SchemaStorage 34 | { 35 | self::$schemaStorage->addSchema($id, $schema); 36 | 37 | return self::$schemaStorage; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/AssertTraitTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace EnricoStahn\JsonAssert\Tests; 13 | 14 | use PHPUnit\Framework\ExpectationFailedException; 15 | use PHPUnit\Framework\TestCase; 16 | use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; 17 | 18 | class AssertTraitTest extends TestCase 19 | { 20 | /** 21 | * Showcase for the Wiki. 22 | * 23 | * @see https://github.com/estahn/phpunit-json-assertions/wiki/assertJsonMatchesSchema 24 | */ 25 | public function testAssertJsonMatchesSchemaSimple() 26 | { 27 | $content = json_decode(file_get_contents(Utils::getJsonPath('assertJsonMatchesSchema_simple.json'))); 28 | 29 | AssertTraitImpl::assertJsonMatchesSchema($content, Utils::getSchemaPath('assertJsonMatchesSchema_simple.schema.json')); 30 | } 31 | 32 | public function testAssertJsonMatchesSchema() 33 | { 34 | $content = json_decode('{"foo":123}'); 35 | 36 | AssertTraitImpl::assertJsonMatchesSchema($content, Utils::getSchemaPath('test.schema.json')); 37 | } 38 | 39 | public function testAssertJsonMatchesSchemaFail() 40 | { 41 | $this->expectException(ExpectationFailedException::class); 42 | $content = json_decode('{"foo":"123"}'); 43 | 44 | AssertTraitImpl::assertJsonMatchesSchema($content, Utils::getSchemaPath('test.schema.json')); 45 | } 46 | 47 | public function testAssertThrowsFileNotFoundException() 48 | { 49 | $this->expectException(FileNotFoundException::class); 50 | $content = json_decode('{"foo":"123"}'); 51 | 52 | AssertTraitImpl::assertJsonMatchesSchema($content, 'not-found.json'); 53 | } 54 | 55 | public function testAssertJsonMatchesSchemaFailMessage() 56 | { 57 | $content = json_decode('{"foo":"123"}'); 58 | 59 | $exception = null; 60 | 61 | try { 62 | AssertTraitImpl::assertJsonMatchesSchema($content, Utils::getSchemaPath('test.schema.json')); 63 | } catch (ExpectationFailedException $exception) { 64 | self::assertStringContainsString('- Property: foo, Constraint: type, Message: String value found, but an integer is required', $exception->getMessage()); 65 | self::assertStringContainsString('- Response: {"foo":"123"}', $exception->getMessage()); 66 | } 67 | 68 | self::assertInstanceOf('\PHPUnit\Framework\ExpectationFailedException', $exception); 69 | } 70 | 71 | /** 72 | * Tests if referenced schemas are loaded automatically. 73 | */ 74 | public function testAssertJsonMatchesSchemaWithRefs() 75 | { 76 | $content = json_decode('{"code":123, "message":"Nothing works."}'); 77 | 78 | AssertTraitImpl::assertJsonMatchesSchema($content, Utils::getSchemaPath('error.schema.json')); 79 | } 80 | 81 | public function testAssertJsonMatchesSchemaWithRefsFails() 82 | { 83 | $this->expectException(ExpectationFailedException::class); 84 | $content = json_decode('{"code":"123", "message":"Nothing works."}'); 85 | 86 | AssertTraitImpl::assertJsonMatchesSchema($content, Utils::getSchemaPath('error.schema.json')); 87 | } 88 | 89 | public function testAssertJsonMatchesSchemaString() 90 | { 91 | $content = json_decode('{"foo":123}'); 92 | $schema = file_get_contents(Utils::getSchemaPath('test.schema.json')); 93 | 94 | AssertTraitImpl::assertJsonMatchesSchemaString($schema, $content); 95 | } 96 | 97 | /** 98 | * Tests assertJsonValueEquals(). 99 | * 100 | * @dataProvider assertJsonValueEqualsProvider 101 | * 102 | * @param string $expression 103 | * @param mixed $value 104 | */ 105 | public function testAssertJsonValueEquals(string $expression, $value) 106 | { 107 | $content = json_decode(file_get_contents(Utils::getJsonPath('testAssertJsonValueEquals.json'))); 108 | 109 | AssertTraitImpl::assertJsonValueEquals($value, $expression, $content); 110 | } 111 | 112 | public function testAssertWithSchemaStore() 113 | { 114 | $obj = new AssertTraitImpl(); 115 | $obj->setUp(); 116 | 117 | $schemaStore = $obj->testWithSchemaStore('foobar', (object) ['type' => 'string']); 118 | 119 | self::assertInstanceOf('JsonSchema\SchemaStorage', $schemaStore); 120 | self::assertEquals($schemaStore->getSchema('foobar'), (object) ['type' => 'string']); 121 | } 122 | 123 | public function assertJsonValueEqualsProvider(): array 124 | { 125 | return [ 126 | ['foo', '123'], 127 | ['a.b.c[0].d[1][0]', 1], 128 | ]; 129 | } 130 | 131 | public function testAssertJsonValueEqualsFailsOnWrongDataType() 132 | { 133 | $this->expectException(ExpectationFailedException::class); 134 | $content = json_decode(file_get_contents(Utils::getJsonPath('testAssertJsonValueEquals.json'))); 135 | 136 | AssertTraitImpl::assertJsonValueEquals($content, 'a.b.c[0].d[1][0]', '{}'); 137 | } 138 | 139 | /** 140 | * @dataProvider jsonObjectProvider 141 | */ 142 | public function testGetJsonObject($expected, $actual) 143 | { 144 | self::assertEquals($expected, AssertTraitImpl::getJsonObject($actual)); 145 | } 146 | 147 | public function jsonObjectProvider(): array 148 | { 149 | return [ 150 | [[], []], 151 | [[], '[]'], 152 | [new \stdClass(), new \stdClass()], 153 | [new \stdClass(), '{}'], 154 | ]; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /tests/Extension/SymfonyTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace EnricoStahn\JsonAssert\Tests\Extension; 13 | 14 | use EnricoStahn\JsonAssert\Extension\Symfony; 15 | use EnricoStahn\JsonAssert\Tests\Utils; 16 | use PHPUnit\Framework\TestCase; 17 | use Symfony\Component\HttpFoundation\Response; 18 | 19 | class SymfonyTest extends TestCase 20 | { 21 | public function testAssertJsonMatchesSchema() 22 | { 23 | $schema = Utils::getSchemaPath('test.schema.json'); 24 | $response = new Response(file_get_contents(Utils::getJsonPath('simple.json'))); 25 | 26 | Symfony::assertJsonMatchesSchema($schema, $response); 27 | } 28 | 29 | public function testAssertJsonMatchesSchemaString() 30 | { 31 | $schema = file_get_contents(Utils::getSchemaPath('test.schema.json')); 32 | $response = new Response(file_get_contents(Utils::getJsonPath('simple.json'))); 33 | 34 | Symfony::assertJsonMatchesSchemaString($schema, $response); 35 | } 36 | 37 | public function testAssertJsonValueEquals() 38 | { 39 | $response = new Response(file_get_contents(Utils::getJsonPath('simple.json'))); 40 | 41 | Symfony::assertJsonValueEquals(123, 'foo', $response); 42 | } 43 | 44 | public function testAssertJsonResponse() 45 | { 46 | $response = new Response('{}', 200, ['Content-Type' => 'application/json']); 47 | 48 | Symfony::assertJsonResponse($response); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Utils.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace EnricoStahn\JsonAssert\Tests; 13 | 14 | class Utils 15 | { 16 | /** 17 | * Returns the full path of the schema file. 18 | * 19 | * @param string $filename The filename of the schema file 20 | * 21 | * @return string 22 | */ 23 | public static function getSchemaPath(string $filename): string 24 | { 25 | return implode(DIRECTORY_SEPARATOR, [__DIR__, 'schemas', $filename]); 26 | } 27 | 28 | /** 29 | * Returns the full path of the schema file. 30 | * 31 | * @param string $filename The filename of the json file 32 | * 33 | * @return string 34 | */ 35 | public static function getJsonPath(string $filename): string 36 | { 37 | return implode(DIRECTORY_SEPARATOR, [__DIR__, 'json', $filename]); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/json/assertJsonMatchesSchema_simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "id" : 33, 3 | "title" : "This is blog title #33", 4 | "created_at" : "2009-03-24 16:24:32", 5 | "tags" : [ 6 | "foo", 7 | "bar" 8 | ] 9 | } -------------------------------------------------------------------------------- /tests/json/simple.json: -------------------------------------------------------------------------------- 1 | { "foo" : 123 } -------------------------------------------------------------------------------- /tests/json/testAssertJsonValueEquals.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo" : "123", 3 | "a" : { 4 | "b" : { 5 | "c" : [ 6 | { 7 | "d" : [ 8 | 0, 9 | [ 10 | 1, 11 | 2 12 | ] 13 | ] 14 | }, 15 | { 16 | "d" : [ 17 | 3, 18 | 4 19 | ] 20 | } 21 | ] 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /tests/schemas/assertJsonMatchesSchema_simple.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema" : "http://json-schema.org/draft-04/schema#", 3 | "type" : "object", 4 | "required" : [ "id", "title" ], 5 | "additionalProperties" : false, 6 | "properties" : { 7 | "id" : { "type" : "integer" }, 8 | "title" : { "type" : "string" }, 9 | "created_at" : { "type" : "string", "pattern" : "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}" }, 10 | "tags" : { 11 | "type" : "array", 12 | "minItems" : 1, 13 | "items" : { "type" : "string" }, 14 | "uniqueItems" : true 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /tests/schemas/definitions.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema" : "http://json-schema.org/draft-04/schema#", 3 | "definitions" : { 4 | "link" : { 5 | "type" : "object", 6 | "additionalProperties" : false, 7 | "properties" : { 8 | "href" : { "type" : "string" } 9 | } 10 | }, 11 | "error" : { 12 | "type" : "object", 13 | "additionalProperties" : false, 14 | "properties" : { 15 | "code" : { "type" : "integer" }, 16 | "message" : { "type" : "string" } 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /tests/schemas/error.schema.json: -------------------------------------------------------------------------------- 1 | { "$ref" : "definitions.schema.json#/definitions/error" } -------------------------------------------------------------------------------- /tests/schemas/test.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type" : "object", 3 | "properties" : { 4 | "foo" : { 5 | "type" : "integer" 6 | } 7 | }, 8 | "required" : [ "foo" ] 9 | } --------------------------------------------------------------------------------