Подтвердить что ты не робот

Symfony2: ограничения проверки подлинности объекта тестирования

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

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

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

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

Итак, кто-нибудь знает, как либо a) получить контейнер, либо b) создать валидатор внутри unit test?

4b9b3361

Ответ 1

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

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

Я вспомнил, что каждый компонент 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;).

Ответ 2

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

<?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

Ответ 3

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

Ответ 4

С 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

Ответ 5

Ответ (b): Создайте валидатор внутри Unit Test (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.

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

Ответ 6

Ответ в fooobar.com/questions/135641/... должен быть немного изменен для Symfony 4:

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

Ответ 7

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

        // ...
    }

}

Это меньше DRY, чем ответ Matt, поскольку вы будете повторять код (для каждого тестового класса) и часто загружать ядро ​​(для каждого тестового метода), но оно самодостаточно и не требует дополнительных зависимостей, поэтому это зависит по вашим потребностям. Плюс я избавился от статического требования.

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