Symfony2:测试实体验证约束

mbskvtky  于 2023-01-26  发布在  其他
关注(0)|答案(8)|浏览(147)

有没有人有好的方法在Symfony2中对实体的验证约束进行单元测试?
理想情况下,我希望能够访问单元测试中的依赖注入容器,给予我就可以访问验证器服务。一旦我有了验证器服务,我就可以手动运行它:

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

我可以扩展WebTestCase,然后创建一个client来访问文档中的容器,但是感觉不太对。WebTestCaseclient在文档中读取时更多的是作为一个工具来测试操作整体,因此使用它来单元测试实体感觉很糟糕。
那么,有人知道如何a)获取容器或B)在单元测试中创建验证器吗?

pw9qyyiw

pw9qyyiw1#

好吧,既然这得到了两票,我想其他人也感兴趣。
我决定把铲子拿出来,惊喜地发现(至少到目前为止)这一点也不难完成。
我记得每个Symfony2组件都可以在独立模式下使用,因此我可以自己创建验证器。
查看以下文件:https://github.com/symfony/Validator/blob/master/ValidatorFactory.php
我意识到,既然有一个ValidatorFactory,那么创建一个验证器就很简单了(特别是对于由注解完成的验证,我就是这样,尽管如果你看看我上面链接的页面上的docblock,你也会发现验证xml和yml的方法)。
第一:

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

然后:

# Symfony >=2.1
$validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
# Symfony <2.1
$validator = ValidatorFactory::buildDefault()->getValidator();

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

$this->assertEquals(0, count($errors));

我希望这能帮助那些良心不允许只使用WebTestCase的人;).

rseugnpd

rseugnpd2#

最后,我们滚动您自己的基本测试用例,以便从测试用例中访问依赖项容器。

<?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);
    }
}

有了这个基类,您现在可以在测试方法中访问验证器服务:

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

我们决定使用静态函数而不是类构造函数,但是您可以轻松地更改行为,将内核直接示例化到构造函数中,而不是依赖PHPUnit提供的静态方法setUpBeforeClass
另外,请记住,测试用例中的每个测试方法不会彼此隔离,因为容器是为整个测试用例共享的。对容器进行修改可能会影响其他测试方法,但如果您只访问validator服务,则不应出现这种情况。然而,通过这种方式,测试用例将运行得更快,因为您不需要为每个测试方法示例化和引导新内核。
为了便于参考,我们从这个blog post中找到了这个类的灵感。它是用法语写的,但我更愿意把它的功劳归于谁:)
此致,
马特

bttbmeg0

bttbmeg03#

我喜欢Kasheens的回答,但它不再适用于Symfony 2.3。

use Symfony\Component\Validator\Validation;

以及

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

例如,如果您想验证注解,请使用enableAnnotationMapping(),如下所示:

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

剩下的都一样

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

watbbzwu4#

在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 class的良好示例

pxiryf3j

pxiryf3j5#

对于Symfony 4,https://stackoverflow.com/a/41884661/4560833中的答案必须稍微更改:
使用ConstraintValidatorTestCase代替AbstractConstraintValidatorTest

6ojccjat

6ojccjat6#

答案(B):在单元测试(Symfony 2.0)中创建验证程序

如果构建了ConstraintConstraintValidator,则根本不需要任何DI容器。
例如,假设您要测试Symfony的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'))));
  }
}

通过这个,你可以检查任何约束配置的验证器,你既不需要ValidatorFactory,也不需要Symfony内核。
更新:正如@ psyloss指出的,这在Symfony 2.5中不起作用,在Symfony〉= 2.1中也不起作用。ConstraintValidator的接口被更改了:isValid被重命名为validate,并且不再返回布尔值。现在你需要一个ExecutionContextInterface来初始化一个ConstraintValidator,而ConstraintValidator本身至少需要一个GlobalExecutionContextInterface和一个TranslatorInterface...所以基本上不需要太多的工作就不可能了。

kcugc4gi

kcugc4gi7#

我看不出WebTestCase有什么问题,如果你不想要客户端,就不要创建;)但是使用一个可能与实际应用程序不同的服务,这是一个潜在的陷阱。因此,我个人是这样做的:

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 answer那么枯燥--因为您将重复代码(针对每个测试类)并经常 Boot 内核(针对每个测试方法),但它是自包含的,不需要额外的依赖项,因此它取决于您的需要。
而且,当您在希望测试的环境中 Boot 内核时,您肯定拥有应用程序正在使用的相同服务--而不是default或mock。

ifmq2ha2

ifmq2ha28#

如果人们在2023年还在读这篇,更喜欢为Symfony〉3 / 4注入ValidatorInterface。

use Symfony\Component\Validator\Validator\ValidatorInterface;

// ...

$this->validator->validate($myEntity);

相关问题