diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index d8ae5e6cc1671..b9af46e493aa9 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -11,6 +11,7 @@ CHANGELOG * Add the `format` option to the `Ulid` constraint to allow accepting different ULID formats * Add the `WordCount` constraint * Add the `Week` constraint + * Add `CompoundConstraintTestCase` to ease testing Compound Constraints 7.1 --- diff --git a/src/Symfony/Component/Validator/Test/CompoundConstraintTestCase.php b/src/Symfony/Component/Validator/Test/CompoundConstraintTestCase.php new file mode 100644 index 0000000000000..7fb32459586e0 --- /dev/null +++ b/src/Symfony/Component/Validator/Test/CompoundConstraintTestCase.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Test; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Constraints\Compound; +use Symfony\Component\Validator\Constraints\CompoundValidator; +use Symfony\Component\Validator\ConstraintViolationListInterface; +use Symfony\Component\Validator\Context\ExecutionContext; +use Symfony\Component\Validator\Context\ExecutionContextInterface; +use Symfony\Component\Validator\Validation; +use Symfony\Component\Validator\Validator\ValidatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * A test case to ease testing Compound Constraints. + * + * @author Alexandre Daubois + */ +abstract class CompoundConstraintTestCase extends TestCase +{ + protected ValidatorInterface $validator; + protected ?ConstraintViolationListInterface $violationList = null; + protected ExecutionContextInterface $context; + protected string $root; + + private mixed $validatedValue; + + protected function setUp(): void + { + $this->root = 'root'; + $this->validator = $this->createValidator(); + $this->context = $this->createContext($this->validator); + } + + protected function validateValue(mixed $value): void + { + $this->validator->inContext($this->context)->validate($this->validatedValue = $value, $this->createCompound()); + } + + protected function createValidator(): ValidatorInterface + { + return Validation::createValidator(); + } + + protected function createContext(?ValidatorInterface $validator = null): ExecutionContextInterface + { + $translator = $this->createMock(TranslatorInterface::class); + $translator->expects($this->any())->method('trans')->willReturnArgument(0); + + return new ExecutionContext($validator ?? $this->createValidator(), $this->root, $translator); + } + + public function assertViolationsRaisedByCompound(Constraint|array $constraints): void + { + if ($constraints instanceof Constraint) { + $constraints = [$constraints]; + } + + $validator = new CompoundValidator(); + $context = $this->createContext(); + $validator->initialize($context); + + $validator->validate($this->validatedValue, new class($constraints) extends Compound { + public function __construct(private array $testedConstraints) + { + parent::__construct(); + } + + protected function getConstraints(array $options): array + { + return $this->testedConstraints; + } + }); + + $expectedViolations = iterator_to_array($context->getViolations()); + + if (!$expectedViolations) { + throw new ExpectationFailedException(\sprintf('Expected at least one violation for constraint(s) "%s", got none raised.', implode(', ', array_map(fn ($constraint) => $constraint::class, $constraints)))); + } + + $failedToAssertViolations = []; + reset($expectedViolations); + foreach ($this->context->getViolations() as $violation) { + if ($violation != current($expectedViolations)) { + $failedToAssertViolations[] = $violation; + } + + next($expectedViolations); + } + + $this->assertEmpty( + $failedToAssertViolations, + \sprintf('Expected violation(s) for constraint(s) %s to be raised by compound.', + implode(', ', array_map(fn ($violation) => ($violation->getConstraint())::class, $failedToAssertViolations)) + ) + ); + } + + public function assertViolationsCount(int $count): void + { + $this->assertCount($count, $this->context->getViolations()); + } + + protected function assertNoViolation(): void + { + $violationsCount = \count($this->context->getViolations()); + $this->assertSame(0, $violationsCount, \sprintf('No violation expected. Got %d.', $violationsCount)); + } + + abstract protected function createCompound(): Compound; +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CompoundValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CompoundValidatorTest.php index 2f48657b21fc5..9eb5c7add7571 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CompoundValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CompoundValidatorTest.php @@ -11,11 +11,9 @@ namespace Symfony\Component\Validator\Tests\Constraints; -use Symfony\Component\Validator\Constraints\Compound; use Symfony\Component\Validator\Constraints\CompoundValidator; -use Symfony\Component\Validator\Constraints\Length; -use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; +use Symfony\Component\Validator\Tests\Fixtures\DummyCompoundConstraint; class CompoundValidatorTest extends ConstraintValidatorTestCase { @@ -43,14 +41,3 @@ public function testValidateWithConstraints() $this->assertNoViolation(); } } - -class DummyCompoundConstraint extends Compound -{ - protected function getConstraints(array $options): array - { - return [ - new NotBlank(), - new Length(['max' => 3]), - ]; - } -} diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/DummyCompoundConstraint.php b/src/Symfony/Component/Validator/Tests/Fixtures/DummyCompoundConstraint.php new file mode 100644 index 0000000000000..87253f25d93a6 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Fixtures/DummyCompoundConstraint.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Fixtures; + +use Symfony\Component\Validator\Constraints\Compound; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\Regex; + +class DummyCompoundConstraint extends Compound +{ + protected function getConstraints(array $options): array + { + return [ + new NotBlank(), + new Length(['max' => 3]), + new Regex('/[a-z]+/'), + new Regex('/[0-9]+/'), + ]; + } +} diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/DummyCompoundConstraintWithGroups.php b/src/Symfony/Component/Validator/Tests/Fixtures/DummyCompoundConstraintWithGroups.php new file mode 100644 index 0000000000000..8b2693ff6ad15 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Fixtures/DummyCompoundConstraintWithGroups.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Fixtures; + +use Symfony\Component\Validator\Constraints\Compound; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\Regex; + +class DummyCompoundConstraintWithGroups extends Compound +{ + protected function getConstraints(array $options): array + { + return [ + new NotBlank(groups: ['not_blank']), + new Length(['max' => 3], groups: ['max_length']), + ]; + } +} diff --git a/src/Symfony/Component/Validator/Tests/Test/CompoundConstraintTestCaseTest.php b/src/Symfony/Component/Validator/Tests/Test/CompoundConstraintTestCaseTest.php new file mode 100644 index 0000000000000..da62992be9875 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Test/CompoundConstraintTestCaseTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Test; + +use PHPUnit\Framework\ExpectationFailedException; +use Symfony\Component\Validator\Constraints\Compound; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\Regex; +use Symfony\Component\Validator\Test\CompoundConstraintTestCase; +use Symfony\Component\Validator\Tests\Fixtures\DummyCompoundConstraint; + +class CompoundConstraintTestCaseTest extends CompoundConstraintTestCase +{ + protected function createCompound(): Compound + { + return new DummyCompoundConstraint(); + } + + public function testAssertNoViolation() + { + $this->validateValue('ab1'); + + $this->assertNoViolation(); + $this->assertViolationsCount(0); + } + + public function testAssertIsRaisedByCompound() + { + $this->validateValue(''); + + $this->assertViolationsRaisedByCompound(new NotBlank()); + $this->assertViolationsCount(1); + } + + public function testMultipleAssertAreRaisedByCompound() + { + $this->validateValue('1245'); + + $this->assertViolationsRaisedByCompound([ + new Length(max: 3), + new Regex('/[a-z]+/'), + ]); + $this->assertViolationsCount(2); + } + + public function testNoAssertRaisedButExpected() + { + $this->validateValue('azert'); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage("Expected violation(s) for constraint(s) Symfony\Component\Validator\Constraints\Length, Symfony\Component\Validator\Constraints\Regex to be raised by compound."); + $this->assertViolationsRaisedByCompound([ + new Length(max: 5), + new Regex('/^[A-Z]+$/'), + ]); + } + + public function testAssertRaisedByCompoundIsNotExactlyTheSame() + { + $this->validateValue('123'); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Expected violation(s) for constraint(s) Symfony\Component\Validator\Constraints\Regex to be raised by compound.'); + $this->assertViolationsRaisedByCompound(new Regex('/^[a-z]+$/')); + } + + public function testAssertRaisedByCompoundButGotNone() + { + $this->validateValue('123'); + + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Expected at least one violation for constraint(s) "Symfony\Component\Validator\Constraints\Length", got none raised.'); + $this->assertViolationsRaisedByCompound(new Length(max: 5)); + } +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy