Symfony2: test des contraintes de validation d’entité

Est-ce que quelqu’un a un bon moyen de tester les contraintes de validation d’une entité dans Symfony2?

Idéalement, je souhaite avoir access au conteneur d’dependency injection dans le test unitaire, ce qui me donnerait access au service de validation. Une fois que j’ai le service de validation, je peux l’exécuter manuellement:

$errors = $validator->validate($entity); 

Je pourrais étendre WebTestCase , puis créer un client pour accéder au conteneur conformément à la documentation, mais cela ne semble pas correct. WebTestCase et le client lisent dans les docs comme une installation permettant de tester des actions dans leur ensemble et, par conséquent, ils ont l’impression de l’utiliser pour tester une entité.

Alors, est-ce que quelqu’un sait comment soit a) obtenir le conteneur ou b) créer le validateur dans un test unitaire?

Ok, car il y a deux votes, je suppose que d’autres personnes sont intéressées.

J’ai décidé de sortir ma pelle et j’ai été agréablement surpris (pour autant que ce soit) que ce n’était pas difficile du tout.

Je me suis souvenu que chaque composant Symfony2 pouvait être utilisé en mode autonome et que je pouvais donc créer le validateur moi-même.

En regardant les documents à: https://github.com/symfony/Validator/blob/master/ValidatorFactory.php

J’ai réalisé que comme il y avait un ValidatorFactory, il était sortingvial de créer un validateur (surtout pour la validation effectuée par des annotations que je suis, bien que si vous regardez le docblock sur la page liée, vous trouverez également des moyens de valider xml et yml) ).

Premier:

 # Symfony >=2.1 use Symfony\Component\Validator\Validation; # Symfony <2.1 use Symfony\Component\Validator\ValidatorFactory; 

et alors:

 # Symfony >=2.1 Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator(); # Symfony <2.1 $validator = ValidatorFactory::buildDefault()->getValidator(); $errors = $validator->validate($entity); $this->assertEquals(0, count($errors)); 

J'espère que cela aidera quiconque de conscience ne les autorisera pas à utiliser WebTestCase;).

Nous finissons par lancer votre propre scénario de test de base pour accéder au conteneur de dépendance depuis un scénario de test. Voici la classe en question:

 boot(); self::$container = self::$kernel->getContainer(); } public function get($serviceId) { return self::$kernel->getContainer()->get($serviceId); } } 

Avec cette classe de base, vous pouvez maintenant le faire dans vos méthodes de test pour accéder au service de validation:

 $validator = $this->get('validator'); 

Nous avons décidé d’utiliser une fonction statique à la place du constructeur de la classe mais vous pouvez facilement changer le comportement pour instancier le kernel directement dans le constructeur au lieu de vous fier à la méthode statique setUpBeforeClass fournie par PHPUnit.

De plus, gardez à l’esprit que chaque méthode de test dans votre scénario de test ne sera pas isolée, car le conteneur est partagé pour l’ensemble du scénario de test. La modification du conteneur peut avoir un impact sur votre autre méthode de test, mais cela ne devrait pas être le cas si vous n’accédez qu’au service du validator . Cependant, les scénarios de test s’exécuteront plus rapidement car vous n’aurez pas besoin d’instancier et de démarrer un nouveau kernel pour chaque méthode de test.

Par souci de référence, nous trouvons l’inspiration pour ce cours depuis cet article de blog . Il est écrit en français mais je préfère donner crédit à qui il appartient 🙂

Cordialement,
Mat

J’ai aimé la réponse de Kasheens, mais cela ne fonctionne plus pour Symfony 2.3. Il y a peu de changements:

 use Symfony\Component\Validator\Validation; 

et

 $validator = Validation::createValidatorBuilder()->getValidator(); 

Si vous souhaitez valider des annotations par exemple, utilisez enableAnnotationMapping () comme ci-dessous:

 $validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator(); 

le rest rest le même:

 $errors = $validator->validate($entity); $this->assertEquals(0, count($errors)); 

Avec Symfony 2.8, il semble que vous puissiez maintenant utiliser la classe AbstractConstraintValidatorTest de cette manière:

 validator->validate(null, new MyEntity()); $this->assertNoViolation(); } public function testNotValid() { $this->assertViolationRaised(new MyEntity(), MyConstraint::SOME_ERROR_NAME); } } 

Vous avez un bon exemple avec la classe IpValidatorTest

Réponse (b): Créez le validateur dans le test unitaire (Symfony 2.0)

Si vous avez construit une Constraint et un ConstraintValidator vous n’avez besoin d’aucun conteneur DI.

Disons par exemple que vous voulez tester la contrainte Type de Symfony et son TypeValidator . Vous pouvez simplement faire ce qui suit:

 use Symfony\Component\Validator\Constraints\TypeValidator; use Symfony\Component\Validator\Constraints\Type; class TypeValidatorTest extends \PHPUnit_Framework_TestCase { function testIsValid() { // The Validator class. $v = new TypeValidator(); // Call the isValid() method directly and pass a // configured Type Constraint object (options // are passed in an associative array). $this->assertTrue($v->isValid(5, new Type(array('type' => 'integer')))); $this->assertFalse($v->isValid(5, new Type(array('type' => 'ssortingng')))); } } 

Avec cela, vous pouvez vérifier chaque validateur que vous aimez avec n’importe quelle configuration de contrainte. Vous n’avez pas besoin de ValidatorFactory ni du kernel Symfony.

Mise à jour: Comme @psylosss l’a fait remarquer, cela ne fonctionne pas dans Symfony 2.5. Cela ne fonctionne pas non plus dans Symfony> = 2.1. L’interface de ConstraintValidator été modifiée: isValid été renommé pour validate et ne renvoie plus de booléen. Maintenant, vous avez besoin d’une GlobalExecutionContextInterface ExecutionContextInterface pour initialiser un ConstraintValidator qui nécessite au moins une GlobalExecutionContextInterface et une GlobalExecutionContextInterface TranslatorInterface … En gros, ce n’est plus possible sans trop de travail.

La réponse dans https://stackoverflow.com/a/41884661/4560833 doit être un peu modifiée pour Symfony 4:

Utilisez ConstraintValidatorTestCase au lieu de AbstractConstraintValidatorTest .

Je ne vois pas de problème avec WebTestCase. Si vous ne voulez pas de client, n’en créez pas;) Mais en utilisant un service éventuellement différent de celui que votre application utilisera, c’est une fosse potentielle. Donc, personnellement, j’ai fait comme ça:

 class ProductServiceTest extends Symfony\Bundle\FrameworkBundle\Test\WebTestCase { /** * Setup the kernel. * * @return null */ public function setUp() { $kernel = self::getKernelClass(); self::$kernel = new $kernel('dev', true); self::$kernel->boot(); } public function testFoo(){ $em = self::$kernel->getContainer()->get('docsortingne.orm.entity_manager'); $v = self::$kernel->getContainer()->get('validator'); // ... } } 

C’est moins DRY que Matt, car vous répétez le code (pour chaque classe de test) et démarrez souvent le kernel (pour chaque méthode de test), mais il est autonome et ne nécessite aucune dépendance supplémentaire, cela dépend donc de vos besoins. . De plus, je me suis débarrassé de la demande statique.

En outre, vous êtes sûr d’avoir les mêmes services que votre application – pas par défaut ou simulé, au démarrage du kernel dans l’environnement que vous souhaitez tester.