Symfony2: ограничения проверки подлинности объекта тестирования
Есть ли у кого-нибудь хороший способ unit test ограничений проверки подлинности сущности в Symfony2?
В идеале я хочу получить доступ к контейнеру для инъекций зависимостей в unit test, который затем предоставит мне доступ к службе проверки подлинности. Как только у меня есть служба проверки подлинности, я могу запустить ее вручную:
$errors = $validator->validate($entity);
Я мог бы расширить WebTestCase
, а затем создать client
, чтобы добраться до контейнера в соответствии с документами, но это не так. WebTestCase
и client
читаются в документах как средство проверки действий в целом, и поэтому он чувствует себя разбитым, чтобы использовать его для unit test объекта.
Итак, кто-нибудь знает, как либо a) получить контейнер, либо b) создать валидатор внутри unit test?
Ответы
Ответ 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 при загрузке ядра в среде, которую вы хотите протестировать.