10000 [Validator] Add `CompoundConstraintTestCase` to ease testing Compound… · symfony/symfony@87365e3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 87365e3

Browse files
[Validator] Add CompoundConstraintTestCase to ease testing Compound Constraint
1 parent 3d380c9 commit 87365e3

File tree

6 files changed

+274
-14
lines changed

6 files changed

+274
-14
lines changed

src/Symfony/Component/Validator/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ CHANGELOG
1010
* Add a `NoSuspiciousCharacters` constraint to validate a string is not a spoofing attempt
1111
* Add the `countUnit` option to the `Length` constraint to allow counting the string length either by code points (like before, now the default setting `Length::COUNT_CODEPOINTS`), bytes (`Length::COUNT_BYTES`) or graphemes (`Length::COUNT_GRAPHEMES`)
1212
* Add the `filenameMaxLength` option to the `File` constraint
13+
* Add `CompoundConstraintTestCase` to ease testing Compound Constraint
1314

1415
6.2
1516
---
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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\Validator\Test;
13+
14+
use PHPUnit\Framework\ExpectationFailedException;
15+
use PHPUnit\Framework\TestCase;
16+
use Symfony\Component\Validator\Constraint;
17+
use Symfony\Component\Validator\Constraints\Compound;
18+
use Symfony\Component\Validator\Constraints\CompoundValidator;
19+
use Symfony\Component\Validator\ConstraintViolation;
20+
use Symfony\Component\Validator\ConstraintViolationListInterface;
21+
use Symfony\Component\Validator\Context\ExecutionContext;
22+
use Symfony\Component\Validator\Context\ExecutionContextInterface;
23+
use Symfony\Component\Validator\Validation;
24+
use Symfony\Component\Validator\Validator\ValidatorInterface;
25+
use Symfony\Contracts\Translation\TranslatorInterface;
26+
27+
/**
28+
* A test case to ease testing Compound Constraint.
29+
*
30+
* @author Alexandre Daubois <alex.daubois@gmail.com>
31+
*/
32+
abstract class CompoundConstraintTestCase extends TestCase
33+
{
34+
protected ValidatorInterface $validator;
35+
protected ?ConstraintViolationListInterface $violationList = null;
36+
protected ExecutionContextInterface $context;
37+
protected string $root;
38+
39+
private mixed $validatedValue;
40+
41+
protected function setUp(): void
42+
{
43+
$this->root = 'root';
44+
45+
$this->validator = $this->createValidator();
46+
$this->context = $this->createContext($this->validator);
47+
}
48+
49+
protected function validateValue(mixed $value): void
50+
{
51+
$this->validator->inContext($this->context)->validate($this->validatedValue = $value, $this->createCompound());
52+
}
53+
54+
protected function createValidator(): ValidatorInterface
55+
{
56+
return Validation::createValidator();
57+
}
58+
59+
protected function createContext(ValidatorInterface $validator = null): ExecutionContextInterface
60+
{
61+
$translator = $this->createMock(TranslatorInterface::class);
62+
$translator->expects($this->any())->method('trans')->willReturnArgument(0);
63+
64+
return new ExecutionContext(
65+
$validator ?? $this->createValidator(),
66+
$this->root,
67+
$translator
68+
);
69+
}
70+
71+
public function assertViolationsRaisedByCompound(Constraint|array $constraints): void
72+
{
73+
if ($constraints instanceof Constraint) {
74+
$constraints = [$constraints];
75+
}
76+
77+
$validator = new CompoundValidator();
78+
$context = $this->createContext();
79+
$validator->initialize($context);
80+
81+
$validator->validate($this->validatedValue, new class($constraints) extends Compound {
82+
public function __construct(public $constraints)
83+
{
84+
parent::__construct();
85+
}
86+
87+
protected function getConstraints(array $options): array
88+
{
89+
return $this->constraints;
90+
}
91+
});
92+
93+
$expectedViolations = iterator_to_array($context->getViolations());
94+
95+
if (\count($expectedViolations) === 0) {
96+
throw new ExpectationFailedException(\sprintf('Expected violation(s) for constraint(s) %s, got none raised.', implode(', ', array_map(static fn (Constraint $constraint): string => $constraint::class, $constraints))));
97+
}
98+
99+
$failedToAssertViolations = [];
100+
reset($expectedViolations);
101+
foreach ($this->context->getViolations() as $violation) {
102+
if ($violation != \current($expectedViolations)) {
103+
$failedToAssertViolations[] = $violation;
104+
}
105+
106+
next($expectedViolations);
107+
}
108+
109+
$this->assertEmpty(
110+
$failedToAssertViolations,
111+
\sprintf('Expected violation(s) for constraint(s) %s to be raised by compound.',
112+
implode(', ', array_map(static fn (ConstraintViolation $violation): string => ($violation->getConstraint())::class, $failedToAssertViolations))
113+
)
114+
);
115+
}
116+
117+
public function assertViolationsCount(int $count): void
118+
{
119+
$this->assertSame($count, \count($this->context->getViolations()));
120+
}
121+
122+
protected function assertNoViolation()
123+
{
124+
$this->assertSame(0, $violationsCount = \count($this->context->getViolations()), sprintf('0 violation expected. Got %u.', $violationsCount));
125+
}
126+
127+
abstract protected function createCompound(): Compound;
128+
}

src/Symfony/Component/Validator/Tests/Constraints/CompoundValidatorTest.php

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@
1111

1212
namespace Symfony\Component\Validator\Tests\Constraints;
1313

14-
use Symfony\Component\Validator\Constraints\Compound;
1514
use Symfony\Component\Validator\Constraints\CompoundValidator;
16-
use Symfony\Component\Validator\Constraints\Length;
17-
use Symfony\Component\Validator\Constraints\NotBlank;
1815
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
16+
use Symfony\Component\Validator\Tests\Fixtures\DummyCompoundConstraint;
1917

2018
class CompoundValidatorTest extends ConstraintValidatorTestCase
2119
{
@@ -43,14 +41,3 @@ public function testValidateWithConstraints()
4341
$this->assertNoViolation();
4442
}
4543
}
46-
47-
class DummyCompoundConstraint extends Compound
48-
{
49-
protected function getConstraints(array $options): array
50-
{
51-
return [
52-
new NotBlank(),
53-
new Length(['max' => 3]),
54-
];
55-
}
56-
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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\Validator\Tests\Fixtures;
13+
14+
use Symfony\Component\Validator\Constraints\Compound;
15+
use Symfony\Component\Validator\Constraints\Length;
16+
use Symfony\Component\Validator\Constraints\NotBlank;
17+
use Symfony\Component\Validator\Constraints\Regex;
18+
19+
class DummyCompoundConstraint extends Compound
20+
{
21+
protected function getConstraints(array $options): array
22+
{
23+
return [
24+
new NotBlank(),
25+
new Length(['max' => 3]),
26+
new Regex('/[a-z]+/'),
27+
new Regex('/[0-9]+/'),
28+
];
29+
}
30+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+ 1241
namespace Symfony\Component\Validator\Tests\Fixtures;
13+
14+
use Symfony\Component\Validator\Constraints\Compound;
15+
use Symfony\Component\Validator\Constraints\Length;
16+
use Symfony\Component\Validator\Constraints\NotBlank;
17+
use Symfony\Component\Validator\Constraints\Regex;
18+
19+
class DummyCompoundConstraintWithGroups extends Compound
20+
{
21+
protected function getConstraints(array $options): array
22+
{
23+
return [
24+
new NotBlank(groups: ['not_blank']),
25+
new Length(['max' => 3], groups: ['max_length']),
26+
];
27+
}
28+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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\Validator\Tests\Test;
13+
14+
use PHPUnit\Framework\ExpectationFailedException;
15+
use Symfony\Component\Validator\Constraints\Compound;
16+
use Symfony\Component\Validator\Constraints\Length;
17+
use Symfony\Component\Validator\Constraints\NotBlank;
18+
use Symfony\Component\Validator\Constraints\Regex;
19+
use Symfony\Component\Validator\Test\CompoundConstraintTestCase;
20+
use Symfony\Component\Validator\Tests\Fixtures\DummyCompoundConstraint;
21+
22+
class CompoundConstraintTestCaseTest extends CompoundConstraintTestCase
23+
{
24+
protected function createCompound(): Compound
25+
{
26+
return new DummyCompoundConstraint();
27+
}
28+
29+
public function testAssertNoViolation()
30+
{
31+
$this->validateValue('ab1');
32+
33+
$this->assertNoViolation();
34+
$this->assertViolationsCount(0);
35+
}
36+
37+
public function testAssertIsRaisedByCompound()
38+
{
39+
$this->validateValue('');
40+
41+
$this->assertViolationsRaisedByCompound(new NotBlank());
42+
$this->assertViolationsCount(1);
43+
}
44+
45+
46+
public function testMultipleAssertAreRaisedByCompound()
47+
{
48+
$this->validateValue('1245');
49+
50+
$this->assertViolationsRaisedByCompound([
51+
new Length(max: 3),
52+
new Regex('/[a-z]+/'),
53+
]);
54+
$this->assertViolationsCount(2);
55+
}
56+
57+
public function testNoAssertRaisedButExpected()
58+
{
59+
$this->validateValue('azert');
60+
61+
$this->expectException(ExpectationFailedException::class);
62+
$this->expectExceptionMessage("Expected violation(s) for constraint(s) Symfony\Component\Validator\Constraints\Length, Symfony\Component\Validator\Constraints\Regex to be raised by compound.");
63+
$this->assertViolationsRaisedByCompound([
64+
new Length(max: 5),
65+
new Regex('/^[A-Z]+$/'),
66+
]);
67+
}
68+
69+
public function testAssertRaisedByCompoundIsNotExactlyTheSame()
70+
{
71+
$this->validateValue('123');
72+
73+
$this->expectException(ExpectationFailedException::class);
74+
$this->expectExceptionMessage('Expected violation(s) for constraint(s) Symfony\Component\Validator\Constraints\Regex to be raised by compound.');
75+
$this->assertViolationsRaisedByCompound(new Regex('/^[a-z]+$/'));
76+
}
77+
78+
public function testAssertRaisedByCompoundButGotNone()
79+
{
80+
$this->validateValue('123');
81+
82+
$this->expectException(ExpectationFailedException::class);
83+
$this->expectExceptionMessage('Expected violation(s) for constraint(s) Symfony\Component\Validator\Constraints\Length, got none raised.');
84+
$this->assertViolationsRaisedByCompound(new Length(max: 5));
85+
}
86+
}

0 commit comments

Comments
 (0)
0