diff --git a/README.md b/README.md index 239180f..558ed81 100755 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ The following shorthand methods exist on the `Assert` class: * `false(mixed $actual)` - check a given value is equal to the *false* boolean * `null(mixed $actual)` - check a given value is *null* * `instance(string|lang.Type $expected, mixed $actual)` - check a given value is an instance of the given type. +* `matches(string $pattern, mixed $actual)` - verify the given value matches a given regular expression. * `throws(string|lang.Type $expected, callable $actual)` - verify the given callable raises an exception. Expected failures diff --git a/src/main/php/test/Assert.class.php b/src/main/php/test/Assert.class.php index 0fe2352..89198ff 100755 --- a/src/main/php/test/Assert.class.php +++ b/src/main/php/test/Assert.class.php @@ -1,7 +1,7 @@ is(new Instance($expected)); } + /** + * Matches shorthand + * + * @param string $pattern + * @param mixed $actual + * @return void + */ + public static function matches($pattern, $actual) { + (new Assertable($actual))->is(new Matches($pattern)); + } + /** * Throws shorthand * diff --git a/src/main/php/test/assert/Matches.class.php b/src/main/php/test/assert/Matches.class.php index 3eefc31..4915360 100755 --- a/src/main/php/test/assert/Matches.class.php +++ b/src/main/php/test/assert/Matches.class.php @@ -1,14 +1,23 @@ pattern= $pattern; } public function matches($value) { - return preg_match($this->pattern, $value); + if (is_string($value) || is_object($value) && method_exists($value, '__toString')) { + if (false === ($r= preg_match($this->pattern, $value))) { + throw new FormatException('Using '.$this->pattern); + } + return $r > 0; + } + return false; } public function describe($value, $positive) { diff --git a/src/test/php/test/unittest/MatchesTest.class.php b/src/test/php/test/unittest/MatchesTest.class.php new file mode 100755 index 0000000..ec25cb4 --- /dev/null +++ b/src/test/php/test/unittest/MatchesTest.class.php @@ -0,0 +1,45 @@ +matches('Test')); + } + + #[Test, Values(['/A/', '/test/'])] + public function does_not_match($pattern) { + Assert::false((new Matches($pattern))->matches('Test')); + } + + #[Test, Values([null, false, true, 1, -1.5, [[]]])] + public function matches_only_strings($value) { + Assert::false((new Matches('/Test/'))->matches($value)); + } + + #[Test] + public function generally_does_not_match_objects() { + Assert::false((new Matches('/Test/'))->matches($this)); + } + + #[Test] + public function matches_stringable() { + Assert::true((new Matches('/Test/'))->matches(new class() { + public function __toString() { return 'Test'; } + })); + } + + #[Test, Expect(class: FormatException::class, message: 'Using not.a.regex')] + public function invalid_pattern() { + (new Matches('not.a.regex'))->matches('Test'); + } + + #[Test] + public function shorthand() { + Assert::matches('/Test.*/', 'Testing'); + } +} \ No newline at end of file