diff --git a/src/JsonSchema/Constraints/Type.php b/src/JsonSchema/Constraints/Type.php index fe7c0ec4..5fc80a42 100644 --- a/src/JsonSchema/Constraints/Type.php +++ b/src/JsonSchema/Constraints/Type.php @@ -10,6 +10,7 @@ namespace JsonSchema\Constraints; use JsonSchema\Exception\InvalidArgumentException; +use UnexpectedValueException as StandardUnexpectedValueException; /** * The Type Constraints, validates an element against a given type @@ -19,6 +20,21 @@ */ class Type extends Constraint { + /** + * @var array|string[] type wordings for validation error messages + */ + static $wording = array( + 'integer' => 'an integer', + 'number' => 'a number', + 'boolean' => 'a boolean', + 'object' => 'an object', + 'array' => 'an array', + 'string' => 'a string', + 'null' => 'a null', + 'any' => NULL, // validation of 'any' is always true so is not needed in message wording + 0 => NULL, // validation of a false-y value is always true, so not needed as well + ); + /** * {@inheritDoc} */ @@ -56,7 +72,15 @@ public function check($value = null, $schema = null, $path = null, $i = null) } if ($isValid === false) { - $this->addError($path, gettype($value) . " value found, but a " . $type . " is required"); + if (!isset(self::$wording[$type])) { + throw new StandardUnexpectedValueException( + sprintf( + "No wording for %s available, expected wordings are: [%s]", + var_export($type, true), + implode(', ', array_filter(self::$wording))) + ); + } + $this->addError($path, gettype($value) . " value found, but " . self::$wording[$type] . " is required"); } } diff --git a/tests/JsonSchema/Tests/Constraints/TypeTest.php b/tests/JsonSchema/Tests/Constraints/TypeTest.php new file mode 100644 index 00000000..aef76055 --- /dev/null +++ b/tests/JsonSchema/Tests/Constraints/TypeTest.php @@ -0,0 +1,76 @@ + + */ +class TypeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @see testIndefiniteArticleForTypeInTypeCheckErrorMessage + * @return array + */ + public function provideIndefiniteArticlesForTypes() + { + return array( + array('integer', 'an',), + array('number', 'a',), + array('boolean', 'a',), + array('object', 'an',), + array('array', 'an',), + array('string', 'a',), + array('null', 'a', array(), 'array',), + ); + } + + /** + * @dataProvider provideIndefiniteArticlesForTypes + */ + public function testIndefiniteArticleForTypeInTypeCheckErrorMessage($type, $wording, $value = null, $label = 'NULL') + { + $constraint = new Type(); + $constraint->check($value, (object)array('type' => $type)); + $this->assertTypeConstraintError("$label value found, but $wording $type is required", $constraint); + } + + /** + * Helper to assert an error message + * + * @param string $expected + * @param Type $actual + */ + private function assertTypeConstraintError($expected, Type $actual) + { + $actualErrors = $actual->getErrors(); + + $this->assertCount(1, $actualErrors, "Failed to assert that Type has exactly one error to assert the error message against."); + + $actualError = $actualErrors[0]; + + $this->assertInternalType('array', $actualError, sprintf('Failed to assert that Type error is an array, %s given', gettype($actualError))); + + $messageKey = 'message'; + $this->assertArrayHasKey( + $messageKey, $actualError, + sprintf('Failed to assert that Type error has a message key %s.', var_export($messageKey, true)) + ); + + $actualMessage = $actualError[$messageKey]; + + $this->assertEquals($expected, $actualMessage); // first equal for the diff + $this->assertSame($expected, $actualMessage); // the same for the strictness + } +}