Единичное тестирование пользовательского ограничения валидации в Symfony 2.1, но без доступа к контейнеру?
Как я могу unit test ContainsItalianVatinValidator
настраивать валидатор, но w * без доступа к контейнеру * и службе validator
(и, таким образом, создать объект-заглушку)?
class ContainsItalianVatinValidator extends ConstraintValidator
{
/**
* @param mixed $value
* @param \Symfony\Component\Validator\Constraint $constraint
*/
public function validate($value, Constraint $constraint)
{
if (!preg_match('/^[0-9]{11}$/', $value, $matches)) {
$this->context->addViolation($constraint->message, array(
'%string%' => $value
));
}
// Compute and check control code
// ...
}
}
В моем тестовом примере я знаю, что я должен получить доступ к ConstraintViolationList
, но я не знаю, как это сделать из самого валидатора
class ContainsItalianVatinValidatorTest extends \PHPUnit_Framework_TestCase
{
public function testEmptyItalianVatin()
{
$emptyVatin = '';
$validator = new ContainsItalianVatinValidator();
$constraint = new ContainsItalianVatinConstraint();
// Do the validation
$validator->validate($emptyVatin, $constraint);
// How can a get a violation list and call ->count()?
$violations = /* ... */;
// Assert
$this->assertGreaterThan(0, $violations->count());
}
}
Ответы
Ответ 1
Когда вы посмотрите на родительский класс валидатора Symfony\Component\Validator\ConstraintValidator
, вы увидите, что существует метод с именем initialize
, который принимает экземпляр Symfony\Component\Validator\ExecutionContext
в качестве аргумента.
После создания валидатора вы можете вызвать метод initialize
и передать макет контекста в валидатор. Вам не нужно проверять, работает ли метод addViolation
правильно, вам нужно только проверить, вызвано ли оно, и если он вызывается с правильными параметрами. Вы можете сделать это с помощью макетной функциональности PHPUnit.
...
$validator = new ContainsItalianVatinValidator();
$context = $this->getMockBuilder('Symfony\Component\Validator\ExecutionContext')-> disableOriginalConstructor()->getMock();
$context->expects($this->once())
->method('addViolation')
->with($this->equalTo('[message]'), $this->equalTo(array('%string%', '')));
$validator->initialize($context);
$validator->validate($emptyVatin, $constraint);
...
В этом коде вы должны заменить [сообщение] сообщением, хранящимся в $constraint->message
.
Собственно, этот вопрос больше связан с PHPUnit, чем с Symfony. Вы можете найти главу Test Doubles в документации PHPUnit.
Ответ 2
Обновлен для Symfony 2.5+. Добавьте тест для каждого возможного сообщения, которое способ validate()
в вашем валидаторе может добавить со значением, которое вызовет это сообщение.
<?php
namespace AcmeBundle\Tests\Validator\Constraints;
use AcmeBundle\Validator\Constraints\SomeConstraint;
use AcmeBundle\Validator\Constraints\SomeConstraintValidator;
/**
* Exercises SomeConstraintValidator.
*/
class SomeConstraintValidatorTest extends \PHPUnit_Framework_TestCase
{
/**
* Configure a SomeConstraintValidator.
*
* @param string $expectedMessage The expected message on a validation violation, if any.
*
* @return AcmeBundle\Validator\Constraints\SomeConstraintValidator
*/
public function configureValidator($expectedMessage = null)
{
// mock the violation builder
$builder = $this->getMockBuilder('Symfony\Component\Validator\Violation\ConstraintViolationBuilder')
->disableOriginalConstructor()
->setMethods(array('addViolation'))
->getMock()
;
// mock the validator context
$context = $this->getMockBuilder('Symfony\Component\Validator\Context\ExecutionContext')
->disableOriginalConstructor()
->setMethods(array('buildViolation'))
->getMock()
;
if ($expectedMessage) {
$builder->expects($this->once())
->method('addViolation')
;
$context->expects($this->once())
->method('buildViolation')
->with($this->equalTo($expectedMessage))
->will($this->returnValue($builder))
;
}
else {
$context->expects($this->never())
->method('buildViolation')
;
}
// initialize the validator with the mocked context
$validator = new SomeConstraintValidator();
$validator->initialize($context);
// return the SomeConstraintValidator
return $validator;
}
/**
* Verify a constraint message is triggered when value is invalid.
*/
public function testValidateOnInvalid()
{
$constraint = new SomeConstraint();
$validator = $this->configureValidator($constraint->someInvalidMessage);
$validator->validate('someInvalidValue', $constraint);
}
/**
* Verify no constraint message is triggered when value is valid.
*/
public function testValidateOnValid()
{
$constraint = new SomeConstraint();
$validator = $this->configureValidator();
$validator->validate('someValidValue', $constraint);
}
}