From 81cde1c7a6aa57da129e3ac3152853edc8aec752 Mon Sep 17 00:00:00 2001 From: Italo Israel Baeza Cabrera Date: Tue, 15 Sep 2026 01:09:10 -0300 Subject: [PATCH 1/3] [4.x] Adds better validation mechanisms --- README.md | 14 ++-- src/Validation/InteractsWithValidator.php | 22 ++++++ src/Validation/PendingTestValidation.php | 63 ++++++++++++++++ .../Validation/InteractsWithValidatorTest.php | 71 ++++++++++++++++++- 4 files changed, 161 insertions(+), 9 deletions(-) create mode 100644 src/Validation/PendingTestValidation.php diff --git a/README.md b/README.md index 5d4817d..db6429e 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ public function test_something_important(): void ### Validation -This meta-package includes a `InteractsWithValidator` trait, that assert if a rule passes or fails using minimal data. This is useful when creating validation rules and testing them without too much boilerplate. +This meta-package includes a `InteractsWithValidator` trait, that asserts if a rule passes or fails using minimal data through the `validation()` method. This is useful when creating validation rules and testing them without too much boilerplate. ```php use Laragear\MetaTesting\Validation\InteractsWithValidator; @@ -120,10 +120,12 @@ use Laragear\MetaTesting\Validation\InteractsWithValidator; public function test_validation_rule(): void { // Assert the validation rule passes. - $this->assertValidationPasses(['test' => 'foo'],['test' => 'my_rule']); + $this->validation(['test' => 'foo'],['test' => 'my_rule']) + ->assertPasses(); // Assert the validation rule fails. - $this->assertValidationFails(['test' => 'bar'],['test' => 'my_rule']); + $this->validation(['test' => 'bar'],['test' => 'my_rule']) + ->assertFails('The string was not [foo]. Received [bar]'); } ``` @@ -195,10 +197,6 @@ public function test_cast() ### Eloquent Builder -> [!TIP] -> -> The Eloquent Builder is only available for Laravel v11.15.0 and later. - To mock the Eloquent Builder of a Model, you may use the `InteractsWithEloquentBuilder` trait and use the `mockQueryFor()` method to make expectations on the builder itself by just calling the methods to chain. To break the chain, you may use `and()` with the final method to call and the results you want to return through the `andReturn()` or `andReturnUsing()`. @@ -248,7 +246,7 @@ public function test_builder() $this->unmockQueryFor(User::class); return null; - });; + }); }); $this->assertNull(User::find(1)); diff --git a/src/Validation/InteractsWithValidator.php b/src/Validation/InteractsWithValidator.php index 3efdcfc..1ba2bbc 100644 --- a/src/Validation/InteractsWithValidator.php +++ b/src/Validation/InteractsWithValidator.php @@ -2,6 +2,7 @@ namespace Laragear\MetaTesting\Validation; +use function array_key_first; use function is_array; /** @@ -9,6 +10,21 @@ */ trait InteractsWithValidator { + /** + * Creates a pending validation rule. + * + * @param array $data + * @param array> $rule + * + * @example $this->validationRule(['foo' => 'bar'], ['foo' => 'my-custom-rule:with_params']); + */ + public function validation(array $data, array $rule): PendingTestValidation + { + return new PendingTestValidation( + $this, $this->app->make('validator')->make($data, $rule), array_key_first($rule) + ); + } + /** * Runs the validation name with data and rules. * @@ -25,6 +41,9 @@ protected function runValidation(array|string $data, array|string $rules): bool /** * Assert a given rule passes. + * + * @deprecated Use validationRule + * @see $this->validate(...) */ protected function assertValidationPasses(array|string $data, array|string $rules): void { @@ -33,6 +52,9 @@ protected function assertValidationPasses(array|string $data, array|string $rule /** * Assert a given rule fails. + * + * @deprecated Use validationRule + * @see $this->validate(...) */ protected function assertValidationFails(array|string $data, array|string $rules): void { diff --git a/src/Validation/PendingTestValidation.php b/src/Validation/PendingTestValidation.php new file mode 100644 index 0000000..b5a6ade --- /dev/null +++ b/src/Validation/PendingTestValidation.php @@ -0,0 +1,63 @@ +testCase::assertFalse( + $this->validator->fails(), + "The rule [$this->key] didn't pass validation. Errors:".PHP_EOL.'- '. + implode(PHP_EOL.'- ', $this->validator->getMessageBag()->all()) + ); + } + + /** + * Assert a given rule fails, optionally with the given message. + */ + public function assertFails(?string $message = null): void + { + $this->testCase::assertTrue($this->validator->fails(), "The rule [$this->key] passed validation."); + + if ($message === null) { + return; + } + + $messages = $this->validator->getMessageBag()->get($this->key); + + foreach ($messages as $output) { + try { + $this->testCase::assertSame($message, $output); + + return; + } catch (ExpectationFailedException) { + continue; + } + } + + $this->testCase::fail( + "No message is equal to [$message]. Found:".PHP_EOL.'- '.implode(PHP_EOL.'- ', $messages) + ); + } +} diff --git a/tests/Validation/InteractsWithValidatorTest.php b/tests/Validation/InteractsWithValidatorTest.php index f560235..d8d86b0 100644 --- a/tests/Validation/InteractsWithValidatorTest.php +++ b/tests/Validation/InteractsWithValidatorTest.php @@ -17,7 +17,7 @@ protected function setUp(): void Validator::extend('test_rule', function ($key, $value): bool { return $value === 'bar'; - }); + }, 'test failed'); } public function test_validates_rule(): void @@ -47,4 +47,73 @@ public function test_validation_fails_fails(): void $this->assertValidationFails(['foo' => 'bar'], ['foo' => 'test_rule']); } + + public function test_pending_validation_passes(): void + { + $pending = $this->validation(['foo' => 'bar'], ['foo' => 'test_rule']); + + $pending->assertPasses(); + } + + public function test_pending_validation_does_not_passes(): void + { + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage(<<<'MESSAGE' +The rule [foo] didn't pass validation. Errors: +- test failed +MESSAGE + ); + + $pending = $this->validation(['foo' => 'quz'], ['foo' => 'test_rule']); + + $pending->assertPasses(); + } + + public function test_pending_validation_fails(): void + { + $pending = $this->validation(['foo' => 'quz'], ['foo' => 'test_rule']); + + $pending->assertFails(); + } + + public function test_pending_validation_does_not_fail(): void + { + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('The rule [foo] passed validation.'); + + $pending = $this->validation(['foo' => 'bar'], ['foo' => 'test_rule']); + + $pending->assertFails(); + } + + public function test_pending_validation_fails_with_message(): void + { + $pending = $this->validation(['foo' => 'quz'], ['foo' => 'test_rule']); + + $pending->assertFails('test failed'); + } + + public function test_pending_validation_does_not_fail_with_message(): void + { + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage('The rule [foo] passed validation.'); + + $pending = $this->validation(['foo' => 'bar'], ['foo' => 'test_rule']); + + $pending->assertFails('test failed'); + } + + public function test_pending_validation_without_message(): void + { + $this->expectException(AssertionFailedError::class); + $this->expectExceptionMessage(<<<'MESSAGE' +No message is equal to [invalid]. Found: +- test failed +MESSAGE + ); + + $pending = $this->validation(['foo' => 'quz'], ['foo' => 'test_rule']); + + $pending->assertFails('invalid'); + } } From c1d31101208914c0104e3e8c39ee5addb553b0df Mon Sep 17 00:00:00 2001 From: Italo Date: Tue, 15 Sep 2026 04:09:27 +0000 Subject: [PATCH 2/3] Apply fixes from StyleCI [ci skip] [skip ci] --- src/Validation/PendingTestValidation.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Validation/PendingTestValidation.php b/src/Validation/PendingTestValidation.php index b5a6ade..0c962d8 100644 --- a/src/Validation/PendingTestValidation.php +++ b/src/Validation/PendingTestValidation.php @@ -5,7 +5,9 @@ use Illuminate\Contracts\Validation\Validator; use Orchestra\Testbench\TestCase; use PHPUnit\Framework\ExpectationFailedException; + use function implode; + use const PHP_EOL; class PendingTestValidation From c3739cd5b89c8ea136694f75b15d9e265644df49 Mon Sep 17 00:00:00 2001 From: Italo Israel Baeza Cabrera Date: Tue, 15 Sep 2026 01:10:35 -0300 Subject: [PATCH 3/3] [4.x] Fixes style-ci --- .styleci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.styleci.yml b/.styleci.yml index abf0863..9c2b20f 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1,5 +1,5 @@ preset: laravel -version: 8.1 +version: 8.3 finder: exclude: