Symfony2:testing实体validation约束

有没有人有一个很好的方式来unit testing在Symfony2实体的validation约束?

理想情况下,我想要在unit testing中访问dependency injection容器,然后让我访问validation器服务。 一旦我有validation器服务,我可以手动运行它:

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

我可以扩展WebTestCase ,然后根据文档创build一个client来访问容器,但是这样做感觉不对。 WebTestCaseclient在文档中读取更多的工具来testing整个行为 ,因此使用它来unit testing一个实体感觉很WebTestCase

那么,有没有人知道如何a)获取容器或b)在unit testing中创buildvalidation器?

好,因为这得到了两票,我猜其他人有兴趣。

我决定把我的铲子拿出来,并且惊喜地(到目前为止),这一点都不难完成。

我记得每个Symfony2组件都可以在独立模式下使用,因此我可以自己创buildvalidation器。

查看文档: https : //github.com/symfony/Validator/blob/master/ValidatorFactory.php

我意识到,既然有一个ValidatorFactory,创build一个validation器是微不足道的(特别是对于我所做的注释所做的validation,尽pipe如果你看看我链接的页面上的docblock,你也可以findvalidationxml和yml的方法)。

第一:

 use Symfony\Component\Validator\ValidatorFactory; 

接着:

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

我希望这可以帮助良心的任何人不会让他们使用WebTestCase;)。

我们最终将自己的基础testing用例从一个testing用例中访问依赖容器。 在这里,有问题的class级:

 <?php namespace Application\AcmeBundle\Tests; // This assumes that this class file is located at: // src/Application/AcmeBundle/Tests/ContainerAwareUnitTestCase.php // with Symfony 2.0 Standard Edition layout. You may need to change it // to fit your own file system mapping. require_once __DIR__.'/../../../../app/AppKernel.php'; class ContainerAwareUnitTestCase extends \PHPUnit_Framework_TestCase { protected static $kernel; protected static $container; public static function setUpBeforeClass() { self::$kernel = new \AppKernel('dev', true); self::$kernel->boot(); self::$container = self::$kernel->getContainer(); } public function get($serviceId) { return self::$kernel->getContainer()->get($serviceId); } } 

有了这个基类,你现在可以在你的testing方法中访问validation器服务:

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

我们决定使用静态函数而不是类构造函数,但是可以很容易地改变行为来直接将内核实例化到构造函数中,而不是依靠PHPUnit提供的静态方法setUpBeforeClass

另外,请记住,您testing用例中的每个单独的testing方法都不会被隔离,因为整个testing用例共享容器。 对容器进行修改可能会对其他testing方法产生影响,但是如果只访问validator服务,情况就不会如此。 但是,这样,testing用例运行得更快,因为您不需要为每个testing方法实例化和引导新的内核。

为了参考,我们从这个博客文章中find了这个class的灵感。 这是用法语写的,但我更喜欢把它归功于谁:)

问候,
马特

我喜欢Kasheens的回答,但它不再适用于Symfony 2.3。 几乎没有变化:

 use Symfony\Component\Validator\Validation; 

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

例如,如果要validation注释,请使用enableAnnotationMapping(),如下所示:

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

其余的保持不变:

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

在Symfony 2.8中,你现在可以这样使用AbstractConstraintValidatorTest类:

 <?php namespace AppBundle\Tests\Constraints; use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest; use AppBundle\Constraints\MyConstraint; use AppBundle\Constraints\MyConstraintValidator; use AppBundle\Entity\MyEntity; use Symfony\Component\Validator\Validation; class MyConstraintValidatorTest extends AbstractConstraintValidatorTest { protected function getApiVersion() { return Validation::API_VERSION_2_5; } protected function createValidator() { return new MyConstraintValidator(); } public function testIsValid() { $this->validator->validate(null, new MyEntity()); $this->assertNoViolation(); } public function testNotValid() { $this->assertViolationRaised(new MyEntity(), MyConstraint::SOME_ERROR_NAME); } } 

你已经有了IpValidatorTest类的一个很好的例子

答案(b):在unit testing中创buildvalidation器(Symfony 2.0)

如果你build立了一个ConstraintConstraintValidator你根本不需要任何DI容器。

比方说,你想testingSymfony的Type约束,它是TypeValidator 。 您可以简单地执行以下操作:

 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' => 'string')))); } } 

有了这个,你可以用任何约束configuration来检查你喜欢的每个validation器。 你既不需要ValidatorFactory也不需要Symfony内核。

更新:正如@psylosss指出的,这在Symfony 2.5中不起作用。 在Symfony> = 2.1中也不起作用。 ConstraintValidator的接口得到了改变: isValid被重命名为validate并且不再返回布尔值。 现在你需要一个ExecutionContextInterface来初始化一个ConstraintValidator ,它本身至less需要一个GlobalExecutionContextInterface和一个TranslatorInterface …所以基本上不可能没有太多的工作。

我没有看到WebTestCase的问题。 如果你不想要一个客户端,不要创build一个;)但是使用一个可能不同于你的实际应用程序的服务将会使用,这是一个潜在的陷阱。 所以我亲自做了这样的事情:

 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('doctrine.orm.entity_manager'); $v = self::$kernel->getContainer()->get('validator'); // ... } } 

这比Matt的答案要less – 因为你会重复代码(对于每个testing类)并经常启动内核(对于每个testing方法),但是它是独立的,不需要额外的依赖关系,因此取决于你的需求。 另外我摆脱了静态要求。

而且,当你在你想testing的环境中引导内核的时候,你肯定会有和你的应用程序一样的服务 – 而不是默认的或模拟的。