Home > front end >  laravel testing web routes
laravel testing web routes

Time:02-06

i have a web route for manufactures module (Backed route that handle resource)

Route::resource('/admin/manufactures', App\Http\Controllers\Back\ManufacturerController::class);

I have create a create a ManufacturerRequest with a simple rule name=>required

and i want to use Laravel Test to test the resource (CRUD)

In my controller the store method is as follow

public function store(ManufacturerRequest $request)
{
        //db
        $request->validate();

        Manufacturer::create($request->all());
 }

I have a simple test

$response = $this->post('/admin/manufactures' ,
[
   '_token' => csrf_token(),
   'name'   => 'test'
]);
       

$response->assertStatus(200);

which is return 403, but besides that the store Method takes ManufacturerRequest object that handles validation, and in the case of the test i pass an array because it only accepts array.

So how can i create a Test that "simulate" form and pass request to controller in order to test validation and CRUD

I am new to Laravel, so any help appreciated

CodePudding user response:

What you want to do is very easy and is also explained on the Documentation, it is very important that you fully read the documentation so you have a rough idea of what you can do with the framework, specially because you are new with it.

As you did not specify which version you are using, I will be using Laravel 8, but it is roughly the same across the board.

Based on your code:

Resource route

Route::resource('/admin/manufactures', ManufacturerController::class);

Controller

public function store(ManufacturerRequest $request)
{
    //db
    $request->validate();

    Manufacturer::create($request->all());
}

You need to change your controller to:

public function store(ManufacturerRequest $request)
{
    //db

    Manufacturer::create($request->all());
}

Yes, just remove the $request->validate(); as the framework will automatically resolve the FormRequest and authorize and validate. If you read part of the validate explanation you will see this:

So, how are the validation rules evaluated? All you need to do is type-hint the request on your controller method. The incoming form request is validated before the controller method is called, meaning you do not need to clutter your controller with any validation logic.

So, when the first line of the controller is run, it means the FormRequest passed the authorization check and validated the input.

What you can also update on your controller is:

public function store(ManufacturerRequest $request)
{
    //db

    Manufacturer::create($request->validated());
}

See I have changed $request->all() with $request->validated(), validated will only return the fields you have a key on the FormRequest's rules, if you use all you will be passing everything you have on the request (also passing non-validated data and that is not good).

Before you try anything, I recommend you read my answer on this post, so you can have a clearer picture about testing.

So, you are getting a 403 maybe because you have a middleware asking for you to be logged in, and you did not use $this->actingAs().


Just because you did not share the FormRequest rules, I will just give a super small example. If you have a this rule inside:

'name' => ['required', 'string'],

What you can do to test that is:

public function test_manufacturer_is_created(): void
{
    $user = User::factory()->create();

    $response = $this->actingAs($user)
        ->post('/admin/manufactures', ['name' => $name = 'Manufacturer 1']);

    $response->assertSuccessful();

    $this->assertDatabaseHas(
        'manufacturers',
        [
            'name' => $name
        ]
    );
}

/**
 * @depends test_manufacturer_is_created
 */
public function test_unauthorized_error_is_thrown_when_the_user_is_not_logged_in(): void
{
    $response = $this->post('/admin/manufactures', ['name' => 'Manufacturer 1']);

    $response->assertUnauthorized();
}

/**
 * @depends test_manufacturer_is_created
 * @dataProvider invalidDataProvider
 */
public function test_error_should_be_returned_when_invalid_data_is_sent($value, bool $deleteField): void
{
    $user = User::factory()->create();

    $response = $this->actingAs($user)
        ->post(
            '/admin/manufactures',
            ! $deleteField ? ['name' => $value] : []
        );

    $response->assertInvalid(['name']);
}

public function invalidDataProvider(): array
{
    return [
        'Missing name' => [null, true],
        'Empty name' => ['', false],
        'Null name' => [null, false],
        'Array name' => [[], false],
        'Number name' => [123, false],
        'Boolean name' => [true, false],
    ];
}

Have in mind I used a lot of things in here:

  • I have tested if the normal insertion works, if it is checking the a valid name is input (FormRequest rules) and that if the user is not logged in it should throw an unauthorized exception.
  • I have used @depends, that is used to run tests ONLY if the dependant test passes, that way we can prevent running the "negative" tests just because the normal flow did not succeed, so it makes no sense to run the other ones and also get a "the test did not pass".
  • I have also used @dataProvider, that is used to share data with a test, so instead of copy-pasting the test a lot of times with data variation, you just vary what data you want the test to use.
  •  Tags:  
  • Related