Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Testing

After every change you need to make sure that login, checkout and the other scenarios still work. Checking that by hand is slow. Laravel lets you describe a scenario in code: the test sends the request itself and checks the response.

Creating a test

php artisan make:test PostTest

The file appears in tests/Feature. Tests in this folder check the application as a whole: the route, the controller, the database and the template.

Checking a page

namespace Tests\Feature;
 
use Tests\TestCase;
 
class PostTest extends TestCase
{
    public function test_home_page_opens(): void
    {
        $response = $this->get('/');
 
        $response->assertStatus(200);
        $response->assertSee('Welcome');
    }
}

The get() method sends a request inside the application — no real server is needed. The name of a test method starts with test.

Tests with a database

The RefreshDatabase trait creates the tables in a separate test database before the tests and returns it to its initial state after each test. The data is prepared with factories:

use App\Models\Post;
use Illuminate\Foundation\Testing\RefreshDatabase;
 
class PostTest extends TestCase
{
    use RefreshDatabase;
 
    public function test_post_page_shows_title(): void
    {
        $post = Post::factory()->create(['title' => 'First post']);
 
        $this->get("/posts/{$post->id}")
            ->assertOk()
            ->assertSee('First post');
    }
}

Acting as a user

actingAs() runs the request as though the user were already logged in:

public function test_guest_cannot_create_post(): void
{
    $this->post('/posts', ['title' => 'Test'])->assertRedirect('/login');
}
 
public function test_user_can_create_post(): void
{
    $user = User::factory()->create();
 
    $this->actingAs($user)
        ->post('/posts', ['title' => 'Test', 'body' => 'Text'])
        ->assertRedirect('/posts');
 
    $this->assertDatabaseHas('posts', ['title' => 'Test']);
}

assertDatabaseHas() checks that the record really appeared in the table.

Testing validation and an API

public function test_title_is_required(): void
{
    $user = User::factory()->create();
 
    $this->actingAs($user)
        ->post('/posts', ['title' => ''])
        ->assertSessionHasErrors('title');
}
 
public function test_api_returns_posts(): void
{
    Post::factory()->count(3)->create();
 
    $this->getJson('/api/posts')
        ->assertOk()
        ->assertJsonCount(3, 'data');
}

Running

php artisan test
php artisan test --filter=PostTest

The first command runs every test, the second only the given class.

Common mistake

Without RefreshDatabase the tests write to the database and get in each other's way: the second run fails because a record with that email already exists. And make sure the tests use a separate database — it is set in the phpunit.xml file. Otherwise the trait wipes the database you work with.

Thanks for reading!

Donate to the site — we work hard for you.