Why don’t you start writing tests?
See how easy it is to get started with automated testing in Laravel
There are hundreds of articles on why you should write tests for your code. And I assume you have read them many times.
Many developers realize the importance of writing tests but just do not get started.
Why Not
Mainly because of some misunderstanding.
- They face the fear of doing it wrong.
- They think they’re not good enough (yet) to write tests.
- They are not sure which type of tests apply to their projects.
As someone who has been there, let me tell you these things are temporary. Once you start writing tests, all this will go away.
Why Not Today
If I were to tell you that it would take only 5–10 minutes of your time to get started with testing, would you mind starting now?
Let today be the start of something new.
- William Shakespeare
Okay. So let’s kick off our testing journey with a Laravel application. You may use an existing web application or create a new one.
In fact, you can follow this article even for any other programming language/framework as we are going to discuss the high-level stuff only.
Default tests with Laravel
Laravel comes with a couple of tests out of the box. Open your terminal and type:
php artisan test
And if you haven’t messed anything up in your web app, you would be greeted with something like:
Great, we have a perfect starting point. Let’s write our own tests now.
Some web app code
If you have a fresh new Laravel web application, please append the following code to the routes/web.php
file before writing your first test.
use App\Models\User;
use Illuminate\Http\Request;
// existing code...
Route::post('/users', function (Request $request) {
$validated = $request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'max:255'],
'password' => ['required', 'confirmed'],
]);
User::create($validated);
return back()->with('success', 'User created successfully.');
});
The following things are happening in this code:
- We validate the form data.
- We create the user in the database.
- We redirect the user back with a success message.
And our tests should cover all these.
Create a new test file
We’ll start by creating a new test file. Please execute the following command in your terminal:
php artisan make:test UserTest
A new test file should be created at tests/Feature/UserTest.php
.
Heads up — Resetting the database
If your code and tests play with the database, you should reset your database after each of your tests to ensure that one test cannot affect the results of another one. Laravel makes it effortless by providing a trait that you can add to your test classes.
Please add the following trait in the UserTest.php
file:
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
class UserTest extends TestCase
{
use LazilyRefreshDatabase;
// existing code...
With that done, let’s bake tests.
Your first test — the Happy path
The default test file already covers a test for a GET request. Thus, we can directly write a test for a POST request.
When the user submits the form from the browser, a POST request is made with the inputs and values. We are going to simulate the same thing in our tests.
Please append the following code to the UserTest.php
file:
// existing code...
public function test_new_user_can_be_added()
{
$response = $this->post('/users', [
'name' => 'Gaurav',
'email' => 'gauravmakhecha@gmail.com',
'password' => '123456',
'password_confirmation' => '123456',
]);
$response->assertRedirect('/')
->assertSessionHas('success', 'User created successfully.');
$this->assertDatabaseHas('users', [
'name' => 'Gaurav',
'email' => 'gauravmakhecha@gmail.com',
]);
}
Laravel provides an easy way to make a POST request from your test files using the post()
method. Using that, we make the request and verify that user details are added to the database and the response is a redirect with the proper message in the session.
Wasn’t this straightforward? Let’s try one more.
Test for the validation
How about adding a test to make sure that our validation works as expected? Please append the following code to the UserTest.php
file:
// existing code...
public function test_new_user_data_is_validated()
{
$response = $this->post('/users', [
'name' => '',
'email' => 'not_email_format',
'password' => '123456',
'password_confirmation' => '456789',
]);
$response->assertStatus(302)
->assertSessionHasErrors(['name', 'email', 'password']);
}
Here, we pass invalid data for the form inputs and confirm (assert) that there is a redirect response (HTTP status code 302) and that there are validation errors in the session.
Test for the unique email address
The business requirement is that the email address of each user has to be unique so let us add a test for the same. Append this test to the UserTest.php
file:
// existing code...
public function test_new_user_with_same_email_cannot_be_added()
{
User::factory()->create([
'email' => 'gauravmakhecha@gmail.com',
]);
$response = $this->post('/users', [
'name' => 'Gaurav',
'email' => 'gauravmakhecha@gmail.com',
'password' => '123456',
'password_confirmation' => '123456',
]);
$response->assertStatus(302)
->assertSessionDoesntHaveErrors(['name', 'password'])
->assertSessionHasErrors(['email']);
}
This test uses Laravel Factory to add a user to the database and then tries to make a POST request with the same email address. Let us run tests and see the results.
Oops, it failed. And the reason? We missed adding the unique validation in our code.
The Fix
Now your test is guiding you with the development. (Yes TDD!)
Please make the following update in the routes/web.php
file:
- 'email' => ['required', 'email', 'max:255'],
+ 'email' => ['required', 'email', 'max:255', 'unique:users'],
Once updated, give yourself a pat on the back and run the tests again ( php artisan test
)
What more?
Did you realize you are now officially a developer who writes automated tests? Congrats!
Need more examples? Check this directory from the official Laravel repository for tests related to various modules.
The goal of this article is just to get you started with writing tests. Not to cover different use cases or scenarios. Hence, I limit the practical coding here and would like to share a few other things to help you progress with it.
Patterns that can help you
Many developers aren’t sure for which parts of their code should they write tests. The simplest answer is: Try to write automated tests for all the functionality that you check while manually testing your web app.
In this example, we would have checked the DB entry, the validation messages, and the redirect while manually testing the web app. And that is what our automated tests covered.
Here are a couple of patterns to help you plan your tests:
- Given-When-Then
- 2. Arrange-Act-Assert
Both are similar so you can use any of them. The idea is:
- First, you set the scene (Add existing DB records, log in a user, etc.)
- Then you take an action (Visit a URL, submit a form, etc.)
- Finally, you verify the results/effects (A DB entry, a session call, response status code, etc.)
This way can generally help you in writing most of the test cases.
Moreover, we have a dedicated article about writing tests only for your code to guide you around the test boundaries.
(Not so) Complex Stuff
What we covered in this article are Feature tests. There are many other types of tests (Unit tests, Feature tests, Integration tests, Acceptance tests, etc.).
Do not hurry to learn/use all of them, you don’t have to. You will feel the need yourself if/when the time arrives.
Topics like mocking and stubbing fall in the same category. Believe me, it is not compulsory to use them. But keep in mind they are quite handy for medium to big-size projects. We use them in our projects and can’t live without them.
A Note on PEST
We use PEST for writing tests but I decided not to include that in this article to keep things simple. I wish (and am sure) it would come out of the box with Laravel.
To The Moon
I hope this article gets you started with writing tests and you never stop in the future. Feel free to comment below in case of any questions. May those tools give you more testing features 😉
And if any of your friends are also giving excuses to start writing tests, how about sharing this article with them for the push?
Cheers and regards.
Originally published at https://blog.freshbits.in.
Sign up for our Free Weekly Newsletter. Fresh content from developers all over the world, fresh and direct from the field! Don’t forget to follow our publication The Dev Project.