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 PostTestThe 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=PostTestThe 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.