Skip to content

Commit 36e08c9

Browse files
authored
Added Symfony validator assertions (#189)
1 parent 3bbf45c commit 36e08c9

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"symfony/security-csrf": "^5.4 | ^6.4 | ^7.0",
5050
"symfony/security-http": "^5.4 | ^6.4 | ^7.0",
5151
"symfony/twig-bundle": "^5.4 | ^6.4 | ^7.0",
52+
"symfony/validator": "^5.4 | ^6.4 | ^7.0",
5253
"symfony/var-exporter": "^5.4 | ^6.4 | ^7.0",
5354
"vlucas/phpdotenv": "^4.2 | ^5.4"
5455
},

src/Codeception/Module/Symfony.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Codeception\Module\Symfony\SessionAssertionsTrait;
2525
use Codeception\Module\Symfony\TimeAssertionsTrait;
2626
use Codeception\Module\Symfony\TwigAssertionsTrait;
27+
use Codeception\Module\Symfony\ValidatorAssertionsTrait;
2728
use Codeception\TestInterface;
2829
use Doctrine\ORM\EntityManagerInterface;
2930
use Exception;
@@ -145,6 +146,7 @@ class Symfony extends Framework implements DoctrineProvider, PartedModule
145146
use SessionAssertionsTrait;
146147
use TimeAssertionsTrait;
147148
use TwigAssertionsTrait;
149+
use ValidatorAssertionsTrait;
148150

149151
public Kernel $kernel;
150152

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Codeception\Module\Symfony;
6+
7+
use Symfony\Component\Validator\ConstraintViolationInterface;
8+
use Symfony\Component\Validator\Validator\ValidatorInterface;
9+
10+
trait ValidatorAssertionsTrait
11+
{
12+
/**
13+
* Asserts that the given subject fails validation.
14+
* This assertion does not concern the exact number of violations.
15+
*
16+
* ```php
17+
* <?php
18+
* $I->dontSeeViolatedConstraint($subject);
19+
* $I->dontSeeViolatedConstraint($subject, 'propertyName');
20+
* $I->dontSeeViolatedConstraint($subject, 'propertyName', 'Symfony\Validator\ConstraintClass');
21+
* ```
22+
*/
23+
public function dontSeeViolatedConstraint(mixed $subject, ?string $propertyPath = null, ?string $constraint = null): void
24+
{
25+
$violations = $this->getViolationsForSubject($subject, $propertyPath, $constraint);
26+
$this->assertCount(0, $violations, 'Constraint violations found.');
27+
}
28+
29+
/**
30+
* Asserts that the given subject passes validation.
31+
* This assertion does not concern the exact number of violations.
32+
*
33+
* ```php
34+
* <?php
35+
* $I->seeViolatedConstraint($subject);
36+
* $I->seeViolatedConstraint($subject, 'propertyName');
37+
* $I->seeViolatedConstraint($subject, 'propertyName', 'Symfony\Validator\ConstraintClass');
38+
* ```
39+
*/
40+
public function seeViolatedConstraint(mixed $subject, ?string $propertyPath = null, ?string $constraint = null): void
41+
{
42+
$violations = $this->getViolationsForSubject($subject, $propertyPath, $constraint);
43+
$this->assertNotCount(0, $violations, 'No constraint violations found.');
44+
}
45+
46+
/**
47+
* Asserts the exact number of violations for the given subject.
48+
*
49+
* ```php
50+
* <?php
51+
* $I->seeViolatedConstraintsCount(3, $subject);
52+
* $I->seeViolatedConstraintsCount(2, $subject, 'propertyName');
53+
* ```
54+
*/
55+
public function seeViolatedConstraintsCount(int $expected, mixed $subject, ?string $propertyPath = null, ?string $constraint = null): void
56+
{
57+
$violations = $this->getViolationsForSubject($subject, $propertyPath, $constraint);
58+
$this->assertCount($expected, $violations);
59+
}
60+
61+
/**
62+
* Asserts that a constraint violation message or a part of it is present in the subject's violations.
63+
*
64+
* ```php
65+
* <?php
66+
* $I->seeViolatedConstraintMessage('too short', $user, 'address');
67+
* ```
68+
*/
69+
public function seeViolatedConstraintMessage(string $expected, mixed $subject, string $propertyPath): void
70+
{
71+
$violations = $this->getViolationsForSubject($subject, $propertyPath);
72+
$containsExpected = false;
73+
foreach ($violations as $violation) {
74+
if ($violation->getPropertyPath() === $propertyPath && str_contains($violation->getMessage(), $expected)) {
75+
$containsExpected = true;
76+
break;
77+
}
78+
}
79+
80+
$this->assertTrue($containsExpected, 'The violation messages do not contain: ' . $expected);
81+
}
82+
83+
/** @return ConstraintViolationInterface[] */
84+
protected function getViolationsForSubject(mixed $subject, ?string $propertyPath = null, ?string $constraint = null): array
85+
{
86+
$validator = $this->getValidatorService();
87+
$violations = $propertyPath ? $validator->validateProperty($subject, $propertyPath) : $validator->validate($subject);
88+
89+
$violations = iterator_to_array($violations);
90+
91+
if ($constraint !== null) {
92+
return array_filter(
93+
$violations,
94+
static fn($violation): bool => $violation->getConstraint()::class === $constraint &&
95+
($propertyPath === null || $violation->getPropertyPath() === $propertyPath)
96+
);
97+
}
98+
99+
return $violations;
100+
}
101+
102+
protected function getValidatorService(): ValidatorInterface
103+
{
104+
return $this->grabService(ValidatorInterface::class);
105+
}
106+
}

0 commit comments

Comments
 (0)