Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .styleci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
preset: laravel
version: 8.1
version: 8.3

finder:
exclude:
Expand Down
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,18 +112,20 @@ 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;

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]');
}
```

Expand Down Expand Up @@ -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()`.
Expand Down Expand Up @@ -248,7 +246,7 @@ public function test_builder()
$this->unmockQueryFor(User::class);

return null;
});;
});
});

$this->assertNull(User::find(1));
Expand Down
22 changes: 22 additions & 0 deletions src/Validation/InteractsWithValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,29 @@

namespace Laragear\MetaTesting\Validation;

use function array_key_first;
use function is_array;

/**
* @internal
*/
trait InteractsWithValidator
{
/**
* Creates a pending validation rule.
*
* @param array<string, mixed> $data
* @param array<string, string|array<int, string|\Illuminate\Validation\Rule>> $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.
*
Expand All @@ -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
{
Expand All @@ -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
{
Expand Down
65 changes: 65 additions & 0 deletions src/Validation/PendingTestValidation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Laragear\MetaTesting\Validation;

use Illuminate\Contracts\Validation\Validator;
use Orchestra\Testbench\TestCase;
use PHPUnit\Framework\ExpectationFailedException;

use function implode;

use const PHP_EOL;

class PendingTestValidation
{
/**
* Create a new Pending Test Validation instance.
*/
public function __construct(
protected TestCase $testCase,
protected Validator $validator,
protected string $key,
) {
//
}

/**
* Assert a given rule passes.
*/
public function assertPasses(): void
{
$this->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)
);
}
}
71 changes: 70 additions & 1 deletion tests/Validation/InteractsWithValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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');
}
}
Loading