Refactor ValidationException

Make the ValidationException a little bit less mutable than before. All
its dependencies are now passed into the constructor.

This commit also make the Factory pass the translator to the exceptions
allowing to define the translator before the exception gets created.
This change is not the ideal one, later I would like to not need the
Singleton from the Factory to do that, but for now it seems like a good
approach.

One more thing that this commit does is to introduce the "id" for
Exceptions. Key can be either the defined "name" or the name of the rule
that throwed the exception. This method will be handy to identify
exceptions better.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
Henrique Moody 2018-05-24 07:47:09 +02:00
parent d7ebb8c1a7
commit 8c529c433e
No known key found for this signature in database
GPG key ID: 221E9281655813A6
35 changed files with 417 additions and 317 deletions

View file

@ -132,18 +132,14 @@ $usernameValidator->validate('#$%'); //false
- All validation exceptions extend this class
- Available methods:
- `getMessage()`;
- `setMode($mode)`;
- `setName($name)`;
- `setParam($name, $value)`;
- `setTemplate($template)`;
- more...
- `updateMode($mode)`;
- `updateTemplate($template)`;
- `Respect\Validation\Exceptions\NestedValidationException`:
- Extends the `Respect\Validation\Exceptions\ValidationException` class
- Usually thrown when the `assert()` fails
- Available methods:
- `getFullMessage()`;
- `getMessages()`;
- more...
## Informative exceptions
@ -199,20 +195,18 @@ Array
You're also able to translate your message to another language with Validation.
The only thing one must do is to define the param `translator` as a callable that
will handle the translation:
will handle the translation overwriting the default factory:
```php
$exception->setParam('translator', 'gettext');
Factory::setDefaultInstance(new Factory([], [], 'gettext'));
```
The example above uses `gettext()` but you can use any other callable value, like
`[$translator, 'trans']` or `you_custom_function()`.
After that, if you call `getMessage()` or `getFullMessage()` (for nested),
After that, if you call `getMessage()`, `getMessages()`, or `getFullMessage()`,
the message will be translated.
Note that `getMessage()` will keep the original message.
## Custom rules
You can also create and use your own rules. To do this, you will need to create

View file

@ -28,7 +28,7 @@ class AlphaException extends ValidationException
],
];
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
return $this->getParam('additionalChars') ? static::EXTRA : static::STANDARD;
}

View file

@ -29,7 +29,7 @@ class AttributeException extends NestedValidationException implements NonOmissib
],
];
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
return $this->getParam('hasReference') ? static::INVALID : static::NOT_PRESENT;
}

View file

@ -28,7 +28,7 @@ class CreditCardException extends ValidationException
],
];
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
if (!$this->getParam('brand')) {
return static::STANDARD;

View file

@ -38,7 +38,7 @@ final class DateTimeException extends ValidationException
/**
* {@inheritdoc}
*/
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
return $this->getParam('format') ? static::FORMAT : static::STANDARD;
}

View file

@ -29,7 +29,7 @@ class GroupedValidationException extends NestedValidationException
],
];
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
$numRules = $this->getParam('passed');
$numFailed = $this->getRelated()->count();

View file

@ -28,7 +28,7 @@ class IpException extends ValidationException
],
];
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
if (!$this->getParam('networkRange')) {
return static::STANDARD;

View file

@ -36,7 +36,7 @@ class KeySetException extends GroupedValidationException implements NonOmissible
/**
* {@inheritdoc}
*/
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
if (0 === $this->getRelated()->count()) {
return static::STRUCTURE;

View file

@ -28,7 +28,7 @@ class KeyValueException extends ValidationException
],
];
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
return $this->getParam('component') ? static::COMPONENT : static::STANDARD;
}

View file

@ -35,7 +35,7 @@ class LengthException extends ValidationException
],
];
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
if (!$this->getParam('minValue')) {
return static::GREATER;

View file

@ -28,7 +28,7 @@ class MaxException extends ValidationException
],
];
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
return $this->getParam('inclusive') ? static::INCLUSIVE : static::STANDARD;
}

View file

@ -28,7 +28,7 @@ class MinException extends ValidationException
],
];
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
return $this->getParam('inclusive') ? static::INCLUSIVE : static::STANDARD;
}

View file

@ -28,8 +28,12 @@ class NotBlankException extends ValidationException
],
];
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
return $this->hasName() ? static::NAMED : static::STANDARD;
if ($this->getParam('input') || $this->getParam('name')) {
return self::NAMED;
}
return self::STANDARD;
}
}

View file

@ -32,8 +32,12 @@ final class NotEmptyException extends ValidationException
],
];
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
return $this->hasName() ? static::NAMED : static::STANDARD;
if ($this->getParam('input') || $this->getParam('name')) {
return self::NAMED;
}
return self::STANDARD;
}
}

View file

@ -28,8 +28,12 @@ class NotOptionalException extends ValidationException
],
];
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
return $this->hasName() ? static::NAMED : static::STANDARD;
if ($this->getParam('input') || $this->getParam('name')) {
return self::NAMED;
}
return self::STANDARD;
}
}

View file

@ -37,8 +37,12 @@ final class NullableException extends ValidationException
/**
* {@inheritdoc}
*/
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
return $this->hasName() ? static::NAMED : static::STANDARD;
if ($this->getParam('input') || $this->getParam('name')) {
return self::NAMED;
}
return self::STANDARD;
}
}

View file

@ -28,8 +28,8 @@ class OptionalException extends ValidationException
],
];
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
return $this->hasName() ? static::NAMED : static::STANDARD;
return $this->getParam('name') ? static::NAMED : static::STANDARD;
}
}

View file

@ -43,7 +43,7 @@ class SizeException extends NestedValidationException
/**
* {@inheritdoc}
*/
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
if (!$this->getParam('minValue')) {
return static::GREATER;

View file

@ -14,10 +14,15 @@ declare(strict_types=1);
namespace Respect\Validation\Exceptions;
use InvalidArgumentException;
use function in_array;
use function is_numeric;
use function call_user_func;
use function Respect\Stringifier\stringify;
/**
* Default exception class for rule validations.
*
* @author Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
class ValidationException extends InvalidArgumentException implements Exception
{
public const MODE_DEFAULT = 'default';
@ -38,14 +43,114 @@ class ValidationException extends InvalidArgumentException implements Exception
],
];
protected $id = 'validation';
protected $mode = self::MODE_DEFAULT;
protected $name = '';
protected $template = '';
protected $params = [];
private $customTemplate = false;
/**
* @var mixed
*/
private $input;
public static function format($template, array $vars = [])
/**
* @var string
*/
private $id;
/**
* @var string
*/
private $mode = self::MODE_DEFAULT;
/**
* @var array
*/
private $params = [];
/**
* @var callable
*/
private $translator;
/**
* @var string
*/
private $template;
public function __construct($input, string $id, array $params, callable $translator)
{
$this->input = $input;
$this->id = $id;
$this->params = $params;
$this->translator = $translator;
$this->template = $this->chooseTemplate();
$this->message = $this->createMessage();
}
public function getId(): string
{
return $this->id;
}
public function getParams(): array
{
return $this->params;
}
public function getParam($name)
{
return $this->params[$name] ?? null;
}
public function updateMode(string $mode): void
{
$this->mode = $mode;
$this->message = $this->createMessage();
}
public function updateTemplate(string $template): void
{
$this->template = $template;
$this->message = $this->createMessage();
}
public function updateParams(array $params): void
{
$this->params = $params;
$this->message = $this->createMessage();
}
public function hasCustomTemplate(): bool
{
return false === isset(static::$defaultTemplates[$this->mode][$this->template]);
}
public function __toString(): string
{
return $this->getMessage();
}
protected function chooseTemplate(): string
{
return key(static::$defaultTemplates[$this->mode]);
}
private function createMessage(): string
{
$template = $this->createTemplate($this->mode, $this->template);
$params = $this->getParams();
$params['name'] = $params['name'] ?? stringify($this->input);
$params['input'] = $this->input;
return $this->format($template, $params);
}
private function createTemplate(string $mode, string $template): string
{
if (isset(static::$defaultTemplates[$mode][$template])) {
$template = static::$defaultTemplates[$mode][$template];
}
return call_user_func($this->translator, $template);
}
private function format($template, array $vars = []): string
{
return preg_replace_callback(
'/{{(\w+)}}/',
@ -64,167 +169,4 @@ class ValidationException extends InvalidArgumentException implements Exception
$template
);
}
public function __toString()
{
return $this->getMainMessage();
}
public function chooseTemplate(): string
{
return key(static::$defaultTemplates[$this->mode]);
}
public function configure($name, array $params = [])
{
$this->setName($name);
$this->setId($this->guessId());
$this->setParams($params);
return $this;
}
public function getName()
{
return $this->name;
}
protected function hasName(): bool
{
$name = $this->getName();
if (is_numeric($name)) {
return (bool) (float) $name;
}
return !in_array($name, ['`FALSE`', '`NULL`', '`{ }`', '""', '']);
}
public function getId()
{
return $this->id;
}
public function getMainMessage()
{
$vars = $this->getParams();
$vars['name'] = $this->getName();
$template = $this->getTemplate();
if (isset($vars['translator']) && is_callable($vars['translator'])) {
$template = call_user_func($vars['translator'], $template);
}
return static::format($template, $vars);
}
public function getParam($name)
{
return $this->hasParam($name) ? $this->params[$name] : false;
}
public function getParams()
{
return $this->params;
}
public function getTemplate()
{
if (!empty($this->template)) {
return $this->template;
}
return $this->template = $this->buildTemplate();
}
public function hasParam($name)
{
return isset($this->params[$name]);
}
public function setId($id)
{
$this->id = $id;
return $this;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function setMode($mode)
{
$this->mode = $mode;
$this->template = $this->buildTemplate();
$this->buildMessage();
return $this;
}
public function setParam($key, $value)
{
$this->params[$key] = $value;
$this->buildMessage();
return $this;
}
public function setParams(array $params)
{
foreach ($params as $key => $value) {
$this->params[$key] = $value;
}
$this->buildMessage();
return $this;
}
public function hasCustomTemplate()
{
return true === $this->customTemplate;
}
public function setTemplate($template)
{
$this->customTemplate = true;
if (isset(static::$defaultTemplates[$this->mode][$template])) {
$template = static::$defaultTemplates[$this->mode][$template];
}
$this->template = $template;
$this->buildMessage();
return $this;
}
private function buildMessage(): void
{
$this->message = $this->getMainMessage();
}
protected function buildTemplate()
{
$templateKey = $this->chooseTemplate();
return static::$defaultTemplates[$this->mode][$templateKey];
}
public function guessId()
{
if (!empty($this->id) && 'validation' != $this->id) {
return $this->id;
}
$pieces = explode('\\', get_called_class());
$exceptionClassShortName = end($pieces);
$ruleClassShortName = str_replace('Exception', '', $exceptionClassShortName);
$ruleName = lcfirst($ruleClassShortName);
return $ruleName;
}
}

View file

@ -31,9 +31,9 @@ class VideoUrlException extends ValidationException
/**
* {@inheritdoc}
*/
public function chooseTemplate(): string
protected function chooseTemplate(): string
{
if (false !== $this->getParam('service')) {
if ($this->getParam('service')) {
return self::SERVICE;
}

View file

@ -14,6 +14,7 @@ declare(strict_types=1);
namespace Respect\Validation;
use ReflectionClass;
use ReflectionException;
use ReflectionObject;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Exceptions\InvalidClassException;
@ -22,7 +23,7 @@ use function array_map;
use function array_merge;
use function array_unique;
use function class_exists;
use function Respect\Stringifier\stringify;
use function lcfirst;
/**
* Factory of objects.
@ -58,6 +59,11 @@ final class Factory
*/
private $exceptionsNamespaces = [];
/**
* @var callable
*/
private $translator;
/**
* Initializes the factory with the defined namespaces.
*
@ -67,10 +73,14 @@ final class Factory
* @param string[] $rulesNamespaces
* @param string[] $exceptionsNamespaces
*/
public function __construct(array $rulesNamespaces, array $exceptionsNamespaces)
{
public function __construct(
array $rulesNamespaces,
array $exceptionsNamespaces,
callable $translator
) {
$this->rulesNamespaces = $this->filterNamespaces($rulesNamespaces, self::DEFAULT_RULES_NAMESPACES);
$this->exceptionsNamespaces = $this->filterNamespaces($exceptionsNamespaces, self::DEFAULT_EXCEPTIONS_NAMESPACES);
$this->translator = $translator;
}
/**
@ -91,7 +101,13 @@ final class Factory
public static function getDefaultInstance(): self
{
if (null === self::$defaultInstance) {
self::$defaultInstance = new self(self::DEFAULT_RULES_NAMESPACES, self::DEFAULT_EXCEPTIONS_NAMESPACES);
self::$defaultInstance = new self(
self::DEFAULT_RULES_NAMESPACES,
self::DEFAULT_EXCEPTIONS_NAMESPACES,
function (string $message): string {
return $message;
}
);
}
return self::$defaultInstance;
@ -137,18 +153,21 @@ final class Factory
{
$reflection = new ReflectionObject($validatable);
$ruleName = $reflection->getShortName();
$name = $validatable->getName() ?: stringify($input);
$params = ['input' => $input] + $extraParams + $this->extractPropertiesValues($validatable, $reflection);
$id = lcfirst($ruleName);
if ($validatable->getName()) {
$id = $params['name'] = $validatable->getName();
}
foreach ($this->exceptionsNamespaces as $namespace) {
$exceptionName = sprintf('%s\\%sException', $namespace, $ruleName);
if (!class_exists($exceptionName)) {
continue;
}
return $this->createValidationException($exceptionName, $name, $params);
return $this->createValidationException($exceptionName, $id, $input, $params);
}
return $this->createValidationException(ValidationException::class, $name, $params);
return $this->createValidationException(ValidationException::class, $id, $input, $params);
}
/**
@ -159,6 +178,7 @@ final class Factory
* @param string $parentName
*
* @throws InvalidClassException
* @throws ReflectionException
*
* @return ReflectionClass
*/
@ -204,17 +224,22 @@ final class Factory
* Creates a Validation exception.
*
* @param string $exceptionName
* @param mixed $name
* @param string $id
* @param mixed $input
* @param array $params
*
* @throws InvalidClassException
* @throws ReflectionException
*
* @return ValidationException
*/
private function createValidationException(string $exceptionName, $name, array $params): ValidationException
private function createValidationException(string $exceptionName, string $id, $input, array $params): ValidationException
{
$exception = $this->createReflectionClass($exceptionName, ValidationException::class)->newInstance();
$exception->configure($name, $params);
/* @var ValidationException $exception */
$exception = $this->createReflectionClass($exceptionName, ValidationException::class)
->newInstance($input, $id, $params, $this->translator);
if (isset($params['template'])) {
$exception->setTemplate($params['template']);
$exception->updateTemplate($params['template']);
}
return $exception;
@ -232,7 +257,12 @@ final class Factory
foreach ($reflection->getProperties() as $property) {
$property->setAccessible(true);
$values[$property->getName()] = $property->getValue($validatable);
$propertyValue = $property->getValue($validatable);
if (null === $propertyValue) {
continue;
}
$values[$property->getName()] = $propertyValue;
}
if (($parentReflection = $reflection->getParentClass())) {

View file

@ -13,7 +13,7 @@ declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\BaseException;
use Respect\Validation\Exceptions\ComponentException;
class Base extends AbstractRule
{
@ -28,7 +28,7 @@ class Base extends AbstractRule
$max = mb_strlen($this->chars);
if (!is_numeric($base) || $base > $max) {
throw new BaseException(sprintf('a base between 1 and %s is required', $max));
throw new ComponentException(sprintf('a base between 1 and %s is required', $max));
}
$this->base = $base;
}

View file

@ -60,8 +60,9 @@ class KeyValue extends AbstractRule
$params[$key] = $this->baseKey;
}
$params['name'] = $this->comparedKey;
$exception->configure($this->comparedKey, $params);
$exception->updateParams($params);
return $exception;
}

View file

@ -48,9 +48,10 @@ class Not extends AbstractRule
$rule = $this->absorbAllOf($rule, $input);
}
throw $rule
->reportError($input)
->setMode(ValidationException::MODE_NEGATIVE);
$exception = $rule->reportError($input);
$exception->updateMode(ValidationException::MODE_NEGATIVE);
throw $exception;
}
private function absorbAllOf(AllOf $rule, $input)

View file

@ -170,7 +170,7 @@ class Validator extends AllOf
parent::check($input);
} catch (ValidationException $exception) {
if (1 == count($this->getRules()) && $this->template) {
$exception->setTemplate($this->template);
$exception->updateTemplate($this->template);
}
throw $exception;

View file

@ -3,14 +3,16 @@
require 'vendor/autoload.php';
use Respect\Validation\Factory;
use Respect\Validation\Validator as v;
Factory::setDefaultInstance(new Factory([], [], function (string $message): string {
return '{{name}} não deve conter letras (a-z) ou dígitos (0-9)';
}));
try {
v::not(v::alnum())->check('abc123');
} catch (Exception $exception) {
$exception->setParam('translator', function () {
return '{{name}} não deve conter letras (a-z) ou dígitos (0-9)';
});
echo $exception->getMessage();
}
?>

View file

@ -3,10 +3,10 @@
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\NestedValidationException;
use Respect\Validation\Factory;
use Respect\Validation\Validator;
function translatorCallback($message)
{
Factory::setDefaultInstance(new Factory([], [], function (string $message): string {
$messages = [
'All of the required rules must pass for {{name}}' => 'Todas as regras requeridas devem passar para {{name}}',
'{{name}} must be of type string' => '{{name}} deve ser do tipo string',
@ -14,13 +14,11 @@ function translatorCallback($message)
];
return $messages[$message];
}
}));
try {
Validator::stringType()->length(2, 15)->assert(0);
} catch (NestedValidationException $exception) {
$exception->setParam('translator', 'translatorCallback');
echo $exception->getFullMessage();
}
?>

View file

@ -3,22 +3,20 @@
require 'vendor/autoload.php';
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Factory;
use Respect\Validation\Validator;
function translatorCallback($message)
{
Factory::setDefaultInstance(new Factory([], [], function (string $message): string {
$messages = [
'{{name}} must be of type string' => '{{name}} deve ser do tipo string',
];
return $messages[$message];
}
}));
try {
Validator::stringType()->length(2, 15)->check(0);
} catch (ValidationException $exception) {
$exception->setParam('translator', 'translatorCallback');
echo $exception->getMessage();
}
?>

View file

@ -16,6 +16,7 @@ namespace Respect\Validation\Test;
use PHPUnit\Framework\TestCase;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Validatable;
use function sprintf;
/**
* Abstract class to create TestCases for Rules.
@ -88,14 +89,28 @@ abstract class RuleTestCase extends TestCase
->method('assert')
->willReturn($expectedResult);
} else {
$checkException = new ValidationException(
'validatable',
'input',
[],
'trim'
);
$checkException->updateTemplate(sprintf('Exception for %s:check() method', $mockClassName));
$validatableMocked
->expects($this->any())
->method('check')
->willThrowException(new ValidationException('Exception for '.$mockClassName.':check() method'));
->willThrowException($checkException);
$assertException = new ValidationException(
'validatable',
'input',
[],
'trim'
);
$assertException->updateTemplate(sprintf('Exception for %s:assert() method', $mockClassName));
$validatableMocked
->expects($this->any())
->method('assert')
->willThrowException(new ValidationException('Exception for '.$mockClassName.':assert() method'));
->willThrowException($assertException);
}
return $validatableMocked;

View file

@ -61,16 +61,9 @@ class CheckExceptionsTest extends TestCase
sprintf('Expected exception class to exist: %s.', $ruleName)
);
$expectedMessage = 'Test exception message.';
$exceptionObject = new $exceptionClass($expectedMessage);
self::assertInstanceOf(
'Exception',
$exceptionObject,
'Every exception should extend an Exception class.'
);
self::assertInstanceOf(
ValidationException::class,
$exceptionObject,
$reflectionClass = new ReflectionClass($exceptionClass);
self::assertTrue(
$reflectionClass->isSubclassOf(ValidationException::class),
'Every Respect/Validation exception must extend ValidationException.'
);
}

View file

@ -15,20 +15,12 @@ namespace Respect\Validation\Exceptions;
use PHPUnit\Framework\TestCase;
/**
* phpunit has an issue with mocking exceptions when in HHVM:
* https://github.com/sebastianbergmann/phpunit-mock-objects/issues/207.
*/
class PrivateNestedValidationException extends NestedValidationException
{
}
class NestedValidationExceptionTest extends TestCase
{
public function testGetRelatedShouldReturnExceptionAddedByAddRelated(): void
{
$composite = new AttributeException();
$node = new IntValException();
$composite = new AttributeException('input', 'id', [], 'trim');
$node = new IntValException('input', 'id', [], 'trim');
$composite->addRelated($node);
self::assertEquals(1, count($composite->getRelated(true)));
self::assertContainsOnly($node, $composite->getRelated());
@ -36,8 +28,8 @@ class NestedValidationExceptionTest extends TestCase
public function testAddingTheSameInstanceShouldAddJustASingleReference(): void
{
$composite = new AttributeException();
$node = new IntValException();
$composite = new AttributeException('input', 'id', [], 'trim');
$node = new IntValException('input', 'id', [], 'trim');
$composite->addRelated($node);
$composite->addRelated($node);
$composite->addRelated($node);

View file

@ -15,62 +15,180 @@ namespace Respect\Validation\Exceptions;
use PHPUnit\Framework\TestCase;
class ValidationExceptionTest extends TestCase
/**
* @group core
* @covers \Respect\Validation\Exceptions\ValidationException
*
* @author Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class ValidationExceptionTest extends TestCase
{
public function testItImplementsExceptionInterface(): void
/**
* @test
*/
public function itShouldImplementException(): void
{
$validationException = new ValidationException();
self::assertInstanceOf(Exception::class, $validationException);
$sut = new ValidationException('input', 'id', [], 'trim');
self::assertInstanceOf(Exception::class, $sut);
}
/**
* @dataProvider providerForFormat
* @test
*/
public function testFormatShouldReplacePlaceholdersProperly($template, $result, $vars): void
public function itShouldRetrieveId(): void
{
$id = 'my id';
$sut = new ValidationException('input', $id, [], 'trim');
self::assertSame($id, $sut->getId());
}
/**
* @test
*/
public function itShouldRetrieveParams(): void
{
$params = ['foo' => true, 'bar' => 23];
$sut = new ValidationException('input', 'id', $params, 'trim');
self::assertSame($params, $sut->getParams());
}
/**
* @test
*/
public function itShouldRetrieveASingleParameter(): void
{
$name = 'any name';
$value = 'any value';
$sut = new ValidationException('input', 'id', [$name => $value], 'trim');
self::assertSame($value, $sut->getParam($name));
}
/**
* @test
*/
public function itShouldReturnNullWhenParameterCanNotBeFound(): void
{
$sut = new ValidationException('input', 'id', [], 'trim');
self::assertNull($sut->getParam('foo'));
}
/**
* @test
*/
public function itShouldHaveADefaultTemplate(): void
{
$sut = new ValidationException('input', 'id', [], 'trim');
self::assertSame('"input" must be valid', $sut->getMessage());
}
/**
* @test
*/
public function itShouldUpdateMode(): void
{
$sut = new ValidationException('input', 'id', [], 'trim');
$sut->updateMode(ValidationException::MODE_NEGATIVE);
self::assertSame('"input" must not be valid', $sut->getMessage());
}
/**
* @test
*/
public function itShouldUpdateTemplate(): void
{
$template = 'This is my new template';
$sut = new ValidationException('input', 'id', [], 'trim');
$sut->updateTemplate($template);
self::assertEquals($template, $sut->getMessage());
}
/**
* @test
*/
public function itShouldTellWhenHasAsCustomUpdateTemplate(): void
{
$sut = new ValidationException('input', 'id', [], 'trim');
self::assertFalse($sut->hasCustomTemplate());
$sut->updateTemplate('This is my new template');
self::assertTrue($sut->hasCustomTemplate());
}
/**
* @test
*/
public function itShouldUseTranslator(): void
{
$template = ' This is my new template ';
$expected = trim($template);
$sut = new ValidationException('input', 'id', [], 'trim');
$sut->updateTemplate($template);
self::assertEquals($expected, $sut->getMessage());
}
/**
* @test
*/
public function itShouldReplacePlaceholders(): void
{
$sut = new ValidationException('foo', 'id', ['bar' => 1, 'baz' => 2], 'trim');
$sut->updateTemplate('{{name}} {{bar}} {{baz}}');
self::assertEquals(
$result,
ValidationException::format($template, $vars)
'"foo" 1 2',
$sut->getMessage()
);
}
public function testGetMainMessageShouldApplyTemplatePlaceholders(): void
/**
* @test
*/
public function itShouldKeepPlaceholdersThatCanNotReplace(): void
{
$sampleValidationException = new ValidationException();
$sampleValidationException->configure('foo', ['bar' => 1, 'baz' => 2]);
$sampleValidationException->setTemplate('{{name}} {{bar}} {{baz}}');
$sut = new ValidationException('foo', 'id', ['foo' => 1], 'trim');
$sut->updateTemplate('{{name}} {{foo}} {{bar}}');
self::assertEquals(
'foo 1 2',
$sampleValidationException->getMainMessage()
'"foo" 1 {{bar}}',
$sut->getMessage()
);
}
public function testSettingTemplates(): void
/**
* @test
*/
public function itShouldUpdateParams(): void
{
$x = new ValidationException();
$x->configure('bar');
$x->setTemplate('foo');
self::assertEquals('foo', $x->getTemplate());
$sut = new ValidationException('input', 'id', ['foo' => 1], 'trim');
$sut->updateTemplate('{{foo}}');
$sut->updateParams(['foo' => 2]);
self::assertEquals('2', $sut->getMessage());
}
public function providerForFormat()
/**
* @test
*/
public function itShouldConvertToString(): void
{
return [
[
'{{foo}} {{bar}} {{baz}}',
'"hello" "world" "respect"',
['foo' => 'hello', 'bar' => 'world', 'baz' => 'respect'],
],
[
'{{foo}} {{bar}} {{baz}}',
'"hello" {{bar}} "respect"',
['foo' => 'hello', 'baz' => 'respect'],
],
[
'{{foo}} {{bar}} {{baz}}',
'"hello" {{bar}} "respect"',
['foo' => 'hello', 'bot' => 111, 'baz' => 'respect'],
],
];
$sut = new ValidationException('input', 'id', [], 'trim');
self::assertSame('"input" must be valid', (string) $sut);
}
}

View file

@ -37,7 +37,7 @@ final class FactoryTest extends TestCase
*/
public function shouldCreateARuleByNameBasedOnNamespace(): void
{
$factory = new Factory([self::TEST_RULES_NAMESPACE], []);
$factory = new Factory([self::TEST_RULES_NAMESPACE], [], 'trim');
self::assertInstanceOf(Valid::class, $factory->rule('valid'));
}
@ -47,7 +47,7 @@ final class FactoryTest extends TestCase
*/
public function shouldLookUpToAllNamespacesUntilRuleIsFound(): void
{
$factory = new Factory([__NAMESPACE__, self::TEST_RULES_NAMESPACE], []);
$factory = new Factory([__NAMESPACE__, self::TEST_RULES_NAMESPACE], [], 'trim');
self::assertInstanceOf(Valid::class, $factory->rule('valid'));
}
@ -59,7 +59,7 @@ final class FactoryTest extends TestCase
{
$constructorArguments = [true, false, true, false];
$factory = new Factory([self::TEST_RULES_NAMESPACE], []);
$factory = new Factory([self::TEST_RULES_NAMESPACE], [], 'trim');
$rule = $factory->rule('stub', $constructorArguments);
self::assertSame($constructorArguments, $rule->validations);
@ -70,7 +70,7 @@ final class FactoryTest extends TestCase
*/
public function shouldThrowsAnExceptionWhenRuleIsInvalid(): void
{
$factory = new Factory([self::TEST_RULES_NAMESPACE], []);
$factory = new Factory([self::TEST_RULES_NAMESPACE], [], 'trim');
$this->expectException(InvalidClassException::class);
$this->expectExceptionMessage(sprintf('"%s" must be an instance of "%s"', Invalid::class, Validatable::class));
@ -83,7 +83,7 @@ final class FactoryTest extends TestCase
*/
public function shouldThrowsAnExceptionWhenRuleIsNotInstantiable(): void
{
$factory = new Factory([self::TEST_RULES_NAMESPACE], []);
$factory = new Factory([self::TEST_RULES_NAMESPACE], [], 'trim');
$this->expectException(InvalidClassException::class);
$this->expectExceptionMessage(sprintf('"%s" must be instantiable', AbstractClass::class));
@ -96,7 +96,7 @@ final class FactoryTest extends TestCase
*/
public function shouldThrowsAnExceptionWhenRuleIsNotFound(): void
{
$factory = new Factory([self::TEST_RULES_NAMESPACE], []);
$factory = new Factory([self::TEST_RULES_NAMESPACE], [], 'trim');
$this->expectException(ComponentException::class);
$this->expectExceptionMessage('"notFoundRule" is not a valid rule name');
@ -109,7 +109,7 @@ final class FactoryTest extends TestCase
*/
public function shouldCreateExceptionBasedOnRule(): void
{
$factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE]);
$factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE], 'trim');
$rule = new Stub();
$input = 2;
@ -122,7 +122,7 @@ final class FactoryTest extends TestCase
*/
public function shouldLookUpToAllNamespacesUntilExceptionIsCreated(): void
{
$factory = new Factory([], [__NAMESPACE__, self::TEST_EXCEPTIONS_NAMESPACE]);
$factory = new Factory([], [__NAMESPACE__, self::TEST_EXCEPTIONS_NAMESPACE], 'trim');
$rule = new Stub();
$input = 2;
@ -135,7 +135,7 @@ final class FactoryTest extends TestCase
*/
public function shouldCreateValidationExceptionWhenExceptionIsNotFound(): void
{
$factory = new Factory([], []);
$factory = new Factory([], [], 'trim');
$input = 'input';
$rule = new Stub();
@ -147,7 +147,7 @@ final class FactoryTest extends TestCase
*/
public function shouldSetInputAsParameterOfCreatedException(): void
{
$factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE]);
$factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE], 'trim');
$rule = new Stub();
$input = 2;
@ -162,7 +162,7 @@ final class FactoryTest extends TestCase
*/
public function shouldPassPropertiesToCreatedException(): void
{
$factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE]);
$factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE], 'trim');
$validations = [true, false, true, true];
$rule = new Stub(...$validations);
@ -178,7 +178,7 @@ final class FactoryTest extends TestCase
*/
public function shouldSetTemplateWhenTemplateKeyIsDefined(): void
{
$factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE]);
$factory = new Factory([], [self::TEST_EXCEPTIONS_NAMESPACE], 'trim');
$extraParams = [
'template' => 'This is my template',
@ -190,7 +190,7 @@ final class FactoryTest extends TestCase
$exception = $factory->exception($rule, $input, $extraParams);
self::assertSame($extraParams['template'], $exception->getTemplate());
self::assertSame($extraParams['template'], $exception->getMessage());
}
/**
@ -206,7 +206,7 @@ final class FactoryTest extends TestCase
*/
public function shouldBeAbleToOverwriteDefaultInstance(): void
{
$factory = new Factory([], []);
$factory = new Factory([], [], 'trim');
$defaultInstance = Factory::getDefaultInstance();

View file

@ -101,7 +101,7 @@ class AbstractRuleTest extends TestCase
->expects($this->once())
->method('reportError')
->with($input)
->will($this->throwException(new ValidationException()));
->will($this->throwException(new ValidationException($input, 'abstract', [], 'trim')));
$abstractRuleMock->assert($input);
}

View file

@ -46,7 +46,7 @@ class BaseTest extends TestCase
/**
* @dataProvider providerForExceptionBase
* @expectedException \Respect\Validation\Exceptions\BaseException
* @expectedException \Respect\Validation\Exceptions\ComponentException
*/
public function testExceptionBase($base, $input): void
{