Symfony2: тестирование ограничений проверки сущностей - PullRequest
49 голосов
/ 10 августа 2011

Есть ли у кого-нибудь хороший способ для модульного тестирования ограничений проверки сущности в Symfony2?

В идеале я хочу иметь доступ к контейнеру внедрения зависимостей в рамках модульного теста, который затем предоставил бы мне доступ к службе валидатора,Когда у меня есть служба валидатора, я могу запустить ее вручную:

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

Я могу расширить WebTestCase, а затем создать client, чтобы добраться до контейнера в соответствии с документами, однако это не кажется правильным,WebTestCase и client читаются в документах как средство для проверки действий в целом, и поэтому он чувствует себя разбитым, чтобы использовать его для модульного тестирования сущности.

Кто-нибудь знает, как а) получить контейнер или б) создать валидатор внутри юнит-теста?

Ответы [ 7 ]

62 голосов
/ 10 августа 2011

Хорошо, так как это набрало два голоса, я думаю, другие люди заинтересовались.

Я решил вытащить свою лопату и был приятно удивлен (так или иначе), что это совсем не трудно было осуществить.

Я вспомнил, что каждый компонент Symfony2 можно использовать в автономном режиме и, следовательно, я мог бы создать валидатор самостоятельно.

Просмотр документов: https://github.com/symfony/Validator/blob/master/ValidatorFactory.php

Я понял, что, поскольку существует ValidatorFactory, создать валидатор было тривиально (особенно для проверки, выполняемой с помощью аннотаций, которыми я являюсь, хотя, если вы посмотрите на блок документации на странице, на которую я ссылался выше, вы также найдете способы проверки 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;).

37 голосов
/ 10 августа 2011

В итоге мы создаем ваш собственный базовый тестовый набор для доступа к контейнеру зависимостей из тестового набора.Вот этот класс:

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

Мы решили пойти сстатическая функция вместо конструктора класса, но вы можете легко изменить поведение для создания экземпляра ядра непосредственно в конструкторе вместо того, чтобы полагаться на статический метод setUpBeforeClass, предоставляемый PHPUnit.

Кроме того, имейте в виду, что каждыйТестовый метод в вашем тестовом примере не будет изолирован друг от друга, потому что контейнер является общим для всего тестового примера.Внесение изменений в контейнер может повлиять на вас другим методом тестирования, но это не должно иметь место, если вы обращаетесь только к службе validator.Однако таким образом тестовые случаи будут выполняться быстрее, потому что вам не нужно будет создавать экземпляры и загружать новое ядро ​​для каждого метода тестирования.

Ради справки, мы находим вдохновение для этого класса из этого сообщение в блоге .Он написан на французском языке, но я предпочитаю отдать должное тому, кому он принадлежит :)

С уважением,Matt

27 голосов
/ 28 марта 2014

Мне понравился ответ 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));
5 голосов
/ 27 января 2017

В 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

2 голосов
/ 07 июля 2018

Ответ в https://stackoverflow.com/a/41884661/4560833 должен быть немного изменен для Symfony 4:

Используйте ConstraintValidatorTestCase вместо AbstractConstraintValidatorTest.

2 голосов
/ 20 марта 2012

Ответ (b): создать валидатор внутри модульного теста (Symfony 2.0)

Если вы построили Constraint и ConstraintValidator, вам вообще не нужен DI-контейнер.

Скажем, например, что вы хотите проверить ограничение Type от Symfony, и оно 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.

Обновление: как указал @psylosss, это не работает в Symfony 2.5. Это также не работает в Symfony> = 2.1. Интерфейс из ConstraintValidator изменился: isValid был переименован в validate и больше не возвращает логическое значение. Теперь вам нужен ExecutionContextInterface для инициализации ConstraintValidator, который сам требует как минимум GlobalExecutionContextInterface и TranslatorInterface ... Так что в принципе это невозможно уже без чрезмерной работы.

0 голосов
/ 07 декабря 2011

Я не вижу проблемы с 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');

        // ...
    }

}

Это менее СУХОЙ ответ, чем ответ Мэтта - так как вы будете повторять код (для каждого тестового класса) и часто загружать ядро ​​(для каждого тестового метода)), но он самодостаточен и не требует дополнительных зависимостей, поэтому зависит от ваших потребностей.Кроме того, я избавился от статического требования.

Кроме того, вы наверняка будете иметь те же сервисы, которые использует ваше приложение - не по умолчанию или не фиктивно, так как вы загружаете ядро ​​в среде, которую вы хотитетест.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...