jcergolj / laravel-form-request-assertions
Package for unit test laravel form request classes
Installs: 11 654
Dependents: 0
Suggesters: 0
Security: 0
Stars: 21
Watchers: 2
Forks: 1
Open Issues: 1
Type:package
Requires
- php: >=8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.2
- laravel/pint: ^1.15
README
Why
Colin DeCarlo gave a talk on Laracon online 21 about unit testing Laravel form requests classes. If you haven't seen his talk, I recommend that you watch it. He prefers testing form requests as a unit and not as feature tests.I like this approach too.
He asked Freek Van der Herten to convert his gist code to package. Granted, I am not Freek; however, I accepted the challenge, and I did it myself. So this package is just a wrapper for Colin's gist, and I added two methods from Jason's package for asserting that controller has the form request.
Installation
Required PHP >=8.0
composer require --dev jcergolj/laravel-form-request-assertions
Usage
Controller
<?php namespace App\Http\Controllers; use App\Http\Requests\CreatePostRequest; use Illuminate\Http\Request; class PostController extends Controller { public function store(CreatePostRequest $request) { // ... } }
web.php routes
<?php use App\Http\Controllers\PostController; Route::post('posts', [PostController::class, 'store']);
Request
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class CreatePostRequest extends FormRequest { public function authorize() { return $this->user()->id === 1 && $this->post->id === 1; } function rules() { return ['email' => ['required', 'email']]; } }
Add the trait to a unit test
After package installation add the TestableFormRequest
trait
<?php namespace Tests\Unit; use Tests\TestCase; use Jcergolj\FormRequestAssertions\TestableFormRequest; class CreatePostRequestTest extends TestCase { use TestableFormRequest; // ... }
Does the controller have the form request test?
public function controller_has_form_request() { $this->assertActionUsesFormRequest(PostController::class, 'store', CreatePostRequest::class); }
or
public function controller_has_form_request() { $this->post(route('users.store')); $this->assertContainsFormRequest(CreateUserRequest::class); }
Test Validation Rules
public function email_is_required() { $this->createFormRequest(CreatePostRequest::class) ->validate(['email' => '']) ->assertFails(['email' => 'required']) ->assertHasMessage('Email is required', 'required'); $this->createFormRequest(CreatePostRequest::class) ->validate(['password' => 'short']) ->assertFails(['password' => App\Rules\PasswordRule::class]); //custom password rule class }
Test Form Request
/** @test */ function test_post_author_is_authorized() { $author = User::factory()->make(['id' => 1]); $post = Post::factory()->make(['id' => 1]); $this->createFormRequest(CreatePostRequest::class) ->withParam('post', $post) ->actingAs($author) ->assertAuthorized(); }
Extending
If you need additional/custom assertions, you can easily extend the \Jcergolj\FormRequestAssertions\TestFormRequest
class.
- Create a new class, for example:
\Tests\Support\TestFormRequest
extending the\Jcergolj\FormRequestAssertions\TestFormRequest
class.namespace Tests\Support; class TestFormRequest extends \Jcergolj\FormRequestAssertions\TestFormRequest { public function assertSomethingImportant() { // your assertions on `$this->request` } }
- Create a new trait, for example:
\Tests\Traits\TestableFormRequest
using the\Jcergolj\FormRequestAssertions\TestableFormRequest
trait. - Overwrite the
\Jcergolj\FormRequestAssertions\TestableFormRequest::createNewTestFormRequest
method to return an instance of the class created in (1).namespace Tests\Support; trait TestableFormRequest { use \Jcergolj\FormRequestAssertions\TestableFormRequest; protected function createNewTestFormRequest(FormRequest $request): TestFormRequest { return new \Tests\Support\TestFormRequest($request); } }
- Use your custom trait instead of
\Jcergolj\FormRequestAssertions\TestableFormRequest
on your test classes
Available Methods
createFormRequest(string $requestClass, $headers = [])
assertRouteUsesFormRequest(string $routeName, string $formRequest)
assertActionUsesFormRequest(string $controller, string $method, string $form_request)
validate(array $data)
by(Authenticatable $user = null)
actingAs(Authenticatable $user = null)
withParams(array $params)
withParam(string $param, $value)
assertAuthorized()
assertNotAuthorized()
assertPasses()
assertFails($expectedFailedRules = [])
assertHasMessage($message, $rule = null)
getFailedRules()
Contributors
A huge thanks go to Colin and Jason. I created a package from Colin's gist and I copied two methods from Jason's package.
Colin DeCarlo |
Jason McCreary |
Janez Cergolj |