• 6 min read
Basics of Testing in Laravel
In the last blog post we touched on the bare minimum you need to know before starting to write tests in Laravel. But what about the actual tests? In this blog post, we are going to cover how to write tests in Laravel.
Artisan has a command for us to make a test through the CLI. The name of your test should be equal to the operation you’re going to perform your test on, followed with the text ‘Test’.
php artisan make:test UserTest
This will create a new file with the name of UserTest
inside the /tests/Feature
directory, which consists of one method called test_example()
. It will check if the / URI can be reached, and if it can, it will send back the assertStatus()
method.
If you perform the test through the CLI, you’ll see that the unit test has indeed passed the test, because Laravel defines the / URI by default in the web.php
file.
Route::get('/', function () {
return view('welcome');
});
It’s also good to know what will happen if the unit tests do not pass the test. Let’s change the URI to /test
and run the command one more time.
Finally! Our first error message. We received a FAIL
message with an in-depth description. The test should receive a status code of 200, but it received a 404 because the / route has not been found.
Unit tests
Up until this point, we worked with a test from the Feature
subdirectory. Unit tests get stored inside the Unit
subdirectory. A Unit test can be created through the same command we performed inside the CLI, but we need to make sure that we define a flag, which will tell our test that we are going to work with a unit test, rather than a feature test.
php artisan make:test UserTest --unit
I prefer to run unit tests through the authentication scaffolding because it allows us to use the User
model. Let’s perform the following command to pull in the Breeze authentication scaffolding.
composer require laravel/breeze –dev
php artisan breeze:install
php artisan migrate
npm install && npm run dev
Also, make sure that you add the authentication route inside the routes/web.php
file. Laravel Breeze does this automatically, but it is always good to double check whether the routes have been defined or not.
Auth::routes();
HTTP tests
We have already performed the get() method, which is added by default, but you can also perform the post()
, put()
, patch()
or delete()
method. These are all methods that have been defined inside a resource route.
Keep in mind that you do need to return a response object that will represent the HTTP response. This will not be the Illuminate response object that you’re familiar with, but it will be an instance of the Illuminate\Foundation\Testing\TestResponse
, which is a wrapper around the Response object with some additional assertions for testing.
If you perform the php artisan route:list
command, you’ll see that the /register
URI is using an HTTP method of post()
. An example of the post()
method might look like the following:
public function test_if_it_stores_new_users()
{
$response = $this->post('/register', [
'name' => 'Dary',
'email' => 'dary@gmail.com',
'password' => 'dary1234',
'password_confirmation' => 'dary1234'
]);
$response->assertRedirect('/home');
}
Be aware that you’re performing the post()
method from the form of your register view. Therefore, you do need to pass in the password_confirmation, even though it doesn’t exist in the database.
Right here, we are going to ‘fake’ register a user to the database. If the user can be created, the test will be completed successfully.
Database test
Laravel offers a lot of tools and assertions in order to make it easier to test your database driven applications. We have already created tests where we’ve checked if we could insert a new user in the database, which honestly isn’t the most effective way. You rather want to make an HTTP call to the store endpoint and then assert that package exists in the database.
There are two assertions you can use.
The first assertion is the $this->assertDatabaseHas()
, which looks inside a database and checks if the given data exists.
The second assertion is the $this->assertDatabaseMissing()
, which checks whether a given value is missing in the database.
Quick exercise
Check if you can find out what the SQL query will be for both methods.
Answer
the $this->assertDatabaseHas()
method looks like an WHERE statement because MySQL will check inside the database whether the given value exists or not.
We do need to make sure that we create a user inside the database. Let’s do this with a Seeder since the Seeder will be used in the next section as well.
php artisan make:seeder UsersTableSeeder
Replace the run()
method with the following:
public function run()
{
DB::table('users')->insert([
'name' => 'Dary',
'email' => ‘info@darynazar.com’,
'password' => bcrypt('password'),
]);
}
The last step is to run our database seeder through the CLI.
php artisan db:seed –class=UsersTableSeeder
If we navigate back to our Unit/UserTest.php
file, we got to make sure that we create a new method that will use the $this->assertDatabaseHas()
method.
The $this->assertDatabaseHas()
accepts two parameters, the first parameter will be the table name where you want to search through. The second parameter will be an array with a key-value pair. The key will be the column name inside the users
table, while the value will be the value of our name
column.
public function test_if_data_exists_in_database()
{
$this->assertDatabaseHas('users', [
'name' => 'Dary'
]);
}
The $this->assertDatabaseMissing()
method works in the same way, but you got to search for a name that does not exist in the database.
public function test_if_data_does_not_exists_in_database()
{
$this->assertDatabaseHas('users', [
'name' => 'John'
]);
}
Seeding tests
The last type of test that we’ll be performing will be Seeding tests. We’re going to check whether we can seed a single seeder or all seeders at the same time.
To test all seeders, you simply have to add the following line inside a method.
public function test_if_seeders_works()
{
$this->seed();
}
This command is equal to the php artisan db:seed
command, which will seed all seeders available inside the /database/seeders
directory.
Instead of seeding all seeders, you can pass in a parameter inside the seed()
method which will be the class name of a single seeder. This command is equal to the command that we performed before.
public function test_if_seeder_works()
{
$this->seed(UsersTableSeeder::class);
}