Skip to content

Commit 422893b

Browse files
committed
feature #60105 [JsonPath] Add JsonPathAssertionsTrait and related constraints (alexandre-daubois)
This PR was merged into the 7.3 branch. Discussion ---------- [JsonPath] Add `JsonPathAssertionsTrait` and related constraints | Q | A | ------------- | --- | Branch? | 7.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | - | License | MIT Let's leverage JsonPath in test cases. I propose to add a new trait to ease testing JSON strings against JsonPath: ```php use PHPUnit\Framework\TestCase; use Symfony\Component\JsonPath\Test\JsonPathAssertionsTrait; class MyApiTest extends TestCase { use JsonPathAssertionsTrait; public function testItFetchesAllUsers(): void { $json = self::fetchUserCollection(); $this->assertJsonPathCount(3, '$.users[*]', $json); $this->assertJsonPathSame(['Melchior'], '$.users[0].username', $json); $this->assertJsonPathEquals(['30'], '$.users[0].age', $json, 'should return the age as string or int'); } public function testItFetchesOneUser(): void { $json = self::fetchOneUser(); $this->assertJsonPathSame(['Melchior'], '$.user.username', $json); $this->assertJsonPathEquals(['30'], '$.user.age', $json, 'should return the age as string or int'); } /** * Hard-coded for the example, but in a real integration test, you would actually make the API call! */ private static function fetchUserCollection(): string { // Simulate fetching JSON data from an API return <<<JSON { "users": [ { "username": "Melchior", "age": 30 }, { "username": "Gaspard", "age": 50 }, { "username": "Balthazar", "age": 55 } ] } JSON; } private static function fetchOneUser(): string { // Simulate fetching JSON data from an API return <<<JSON { "user": { "username": "Melchior", "age": 30 } } JSON; } } ``` Commits ------- 60cba45 [JsonPath] Add `JsonPathAssertionsTrait` and related constraints
2 parents c11519a + 60cba45 commit 422893b

9 files changed

+557
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\JsonPath\Test;
13+
14+
use PHPUnit\Framework\Assert;
15+
use PHPUnit\Framework\ExpectationFailedException;
16+
use Symfony\Component\JsonPath\JsonPath;
17+
18+
/**
19+
* @author Alexandre Daubois <alex.daubois@gmail.com>
20+
*
21+
* @experimental
22+
*/
23+
trait JsonPathAssertionsTrait
24+
{
25+
/**
26+
* @throws ExpectationFailedException
27+
*/
28+
final public static function assertJsonPathEquals(mixed $expectedValue, JsonPath|string $jsonPath, string $json, string $message = ''): void
29+
{
30+
Assert::assertThat($expectedValue, new JsonPathEquals($jsonPath, $json), $message);
31+
}
32+
33+
/**
34+
* @throws ExpectationFailedException
35+
*/
36+
final public static function assertJsonPathNotEquals(mixed $expectedValue, JsonPath|string $jsonPath, string $json, string $message = ''): void
37+
{
38+
Assert::assertThat($expectedValue, new JsonPathNotEquals($jsonPath, $json), $message);
39+
}
40+
41+
/**
42+
* @throws ExpectationFailedException
43+
*/
44+
final public static function assertJsonPathCount(int $expectedCount, JsonPath|string $jsonPath, string $json, string $message = ''): void
45+
{
46+
Assert::assertThat($expectedCount, new JsonPathCount($jsonPath, $json), $message);
47+
}
48+
49+
/**
50+
* @throws ExpectationFailedException
51+
*/
52+
final public static function assertJsonPathSame(mixed $expectedValue, JsonPath|string $jsonPath, string $json, string $message = ''): void
53+
{
54+
Assert::assertThat($expectedValue, new JsonPathSame($jsonPath, $json), $message);
55+
}
56+
57+
/**
58+
* @throws ExpectationFailedException
59+
*/
60+
final public static function assertJsonPathNotSame(mixed $expectedValue, JsonPath|string $jsonPath, string $json, string $message = ''): void
61+
{
62+
Assert::assertThat($expectedValue, new JsonPathNotSame($jsonPath, $json), $message);
63+
}
64+
65+
/**
66+
* @throws ExpectationFailedException
67+
*/
68+
final public static function assertJsonPathContains(mixed $expectedValue, JsonPath|string $jsonPath, string $json, bool $strict = true, string $message = ''): void
69+
{
70+
Assert::assertThat($expectedValue, new JsonPathContains($jsonPath, $json, $strict), $message);
71+
}
72+
73+
/**
74+
* @throws ExpectationFailedException
75+
*/
76+
final public static function assertJsonPathNotContains(mixed $expectedValue, JsonPath|string $jsonPath, string $json, bool $strict = true, string $message = ''): void
77+
{
78+
Assert::assertThat($expectedValue, new JsonPathNotContains($jsonPath, $json, $strict), $message);
79+
}
80+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\JsonPath\Test;
13+
14+
use PHPUnit\Framework\Constraint\Constraint;
15+
use Symfony\Component\JsonPath\JsonCrawler;
16+
use Symfony\Component\JsonPath\JsonPath;
17+
18+
/**
19+
* @author Alexandre Daubois <alex.daubois@gmail.com>
20+
*
21+
* @experimental
22+
*/
23+
class JsonPathContains extends Constraint
24+
{
25+
public function __construct(
26+
private JsonPath|string $jsonPath,
27+
private string $json,
28+
private bool $strict = true,
29+
) {
30+
}
31+
32+
public function toString(): string
33+
{
34+
return \sprintf('is found in elements at JSON path "%s"', $this->jsonPath);
35+
}
36+
37+
protected function matches(mixed $other): bool
38+
{
39+
$result = (new JsonCrawler($this->json))->find($this->jsonPath);
40+
41+
return \in_array($other, $result, $this->strict);
42+
}
43+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\JsonPath\Test;
13+
14+
use PHPUnit\Framework\Constraint\Constraint;
15+
use Symfony\Component\JsonPath\JsonCrawler;
16+
use Symfony\Component\JsonPath\JsonPath;
17+
18+
/**
19+
* @author Alexandre Daubois <alex.daubois@gmail.com>
20+
*
21+
* @experimental
22+
*/
23+
class JsonPathCount extends Constraint
24+
{
25+
public function __construct(
26+
private JsonPath|string $jsonPath,
27+
private string $json,
28+
) {
29+
}
30+
31+
public function toString(): string
32+
{
33+
return \sprintf('matches expected count of JSON path "%s"', $this->jsonPath);
34+
}
35+
36+
protected function matches(mixed $other): bool
37+
{
38+
return $other === \count((new JsonCrawler($this->json))->find($this->jsonPath));
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\JsonPath\Test;
13+
14+
use PHPUnit\Framework\Constraint\Constraint;
15+
use Symfony\Component\JsonPath\JsonCrawler;
16+
use Symfony\Component\JsonPath\JsonPath;
17+
18+
/**
19+
* @author Alexandre Daubois <alex.daubois@gmail.com>
20+
*
21+
* @experimental
22+
*/
23+
class JsonPathEquals extends Constraint
24+
{
25+
public function __construct(
26+
private JsonPath|string $jsonPath,
27+
private string $json,
28+
) {
29+
}
30+
31+
public function toString(): string
32+
{
33+
return \sprintf('equals JSON path "%s" result', $this->jsonPath);
34+
}
35+
36+
protected function matches(mixed $other): bool
37+
{
38+
return (new JsonCrawler($this->json))->find($this->jsonPath) == $other;
39+
}
40+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\JsonPath\Test;
13+
14+
use PHPUnit\Framework\Constraint\Constraint;
15+
use Symfony\Component\JsonPath\JsonCrawler;
16+
use Symfony\Component\JsonPath\JsonPath;
17+
18+
/**
19+
* @author Alexandre Daubois <alex.daubois@gmail.com>
20+
*
21+
* @experimental
22+
*/
23+
class JsonPathNotContains extends Constraint
24+
{
25+
public function __construct(
26+
private JsonPath|string $jsonPath,
27+
private string $json,
28+
private bool $strict = true,
29+
) {
30+
}
31+
32+
public function toString(): string
33+
{
34+
return \sprintf('is not found in elements at JSON path "%s"', $this->jsonPath);
35+
}
36+
37+
protected function matches(mixed $other): bool
38+
{
39+
$result = (new JsonCrawler($this->json))->find($this->jsonPath);
40+
41+
return !\in_array($other, $result, $this->strict);
42+
}
43+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\JsonPath\Test;
13+
14+
use PHPUnit\Framework\Constraint\Constraint;
15+
use Symfony\Component\JsonPath\JsonCrawler;
16+
use Symfony\Component\JsonPath\JsonPath;
17+
18+
/**
19+
* @author Alexandre Daubois <alex.daubois@gmail.com>
20+
*
21+
* @experimental
22+
*/
23+
class JsonPathNotEquals extends Constraint
24+
{
25+
public function __construct(
26+
private JsonPath|string $jsonPath,
27+
private string $json,
28+
) {
29+
}
30+
31+
public function toString(): string
32+
{
33+
return \sprintf('does not equal JSON path "%s" result', $this->jsonPath);
34+
}
35+
36+
protected function matches(mixed $other): bool
37+
{
38+
return (new JsonCrawler($this->json))->find($this->jsonPath) != $other;
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\JsonPath\Test;
13+
14+
use PHPUnit\Framework\Constraint\Constraint;
15+
use Symfony\Component\JsonPath\JsonCrawler;
16+
use Symfony\Component\JsonPath\JsonPath;
17+
18+
/**
19+
* @author Alexandre Daubois <alex.daubois@gmail.com>
20+
*
21+
* @experimental
22+
*/
23+
class JsonPathNotSame extends Constraint
24+
{
25+
public function __construct(
26+
private JsonPath|string $jsonPath,
27+
private string $json,
28+
) {
29+
}
30+
31+
public function toString(): string
32+
{
33+
return \sprintf('is not identical to JSON path "%s" result', $this->jsonPath);
34+
}
35+
36+
protected function matches(mixed $other): bool
37+
{
38+
return (new JsonCrawler($this->json))->find($this->jsonPath) !== $other;
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\JsonPath\Test;
13+
14+
use PHPUnit\Framework\Constraint\Constraint;
15+
use Symfony\Component\JsonPath\JsonCrawler;
16+
use Symfony\Component\JsonPath\JsonPath;
17+
18+
/**
19+
* @author Alexandre Daubois <alex.daubois@gmail.com>
20+
*
21+
* @experimental
22+
*/
23+
class JsonPathSame extends Constraint
24+
{
25+
public function __construct(
26+
private JsonPath|string $jsonPath,
27+
private string $json,
28+
) {
29+
}
30+
31+
public function toString(): string
32+
{
33+
return \sprintf('is identical to JSON path "%s" result', $this->jsonPath);
34+
}
35+
36+
protected function matches(mixed $other): bool
37+
{
38+
return (new JsonCrawler($this->json))->find($this->jsonPath) === $other;
39+
}
40+
}

0 commit comments

Comments
 (0)