Cover Image
#laravel #testing

Getting Started With Testing In Laravel

~ 6 MINS READ
15th Jan 2022
Mohammed Omer By Mohammed Omer

Introduction

Testing is one of the topics that not only beginners but also some experienced developers ignore, one may think, why would I write code to test if my code is working? isn’t that a waste of time?. Well, this may be the case for a very small proof of concept project, but for bigger and more serious projects and as the project progresses, it will be harder to manage and add changes without risking breaking something mistakingly.

This is a guide for laravel developers who have never written tests for their applications

Here are some reasons why I always write tests for my projects (unless I am tinkering with a new idea):

  1. Testing will give you more confidence that your code is working the way you expect it to work without pulling the browser and testing all the features manually.
  2. You can refactor your code with confidence without worrying about breaking any existing feature. Refactor, run your tests, and see if it breaks anything.
  3. Saving time! yes, this may sound counterintuitive but it is the thing that makes me start any feature with a test. Imagine this scenario: you want to develop a new feature, and you start with a test that describes exactly what you want, something like “when I visit this route I expect the system to be in this state” this is exactly what feature tests are about. Then you start building your feature and run the test to see if it meets your expectations (notice, till this point we haven’t even opened the browser or an API client) you see the cause of the error from the failing test output and you start fixing it until the test is passing. This will save you a huge amount of time and will keep you focused on the feature you are developing.

How do I start testing my laravel app?

Laravel comes pre-configured with the necessary tooling for testing so you don’t need to install any additional libraries. There is a couple of default test classes,tests/Feature/ExampleTest.php and tests/Unit/ExampleTest.php.

let’s assume we want to implement the user registration feature, here is what the workflow may look like:

We will be interacting with the database and we don’t want to use our development database to test our feature because we will be refreshing our database every time we run a test. So to use a lightweight in-memory SQLite database we need to uncomment two lines in phpunit.xml file.

<php>
    <server name="APP_ENV" value="testing"/>
    <server name="BCRYPT_ROUNDS" value="4"/>
    <server name="CACHE_DRIVER" value="array"/>
-   <!-- <server name="DB_CONNECTION" value="sqlite"/> -->
-   <!-- <server name="DB_DATABASE" value=":memory:"/> -->
+   <server name="DB_CONNECTION" value="sqlite"/>
+   <server name="DB_DATABASE" value=":memory:"/>
    <server name="MAIL_MAILER" value="array"/>
    <server name="QUEUE_CONNECTION" value="sync"/>
    <server name="SESSION_DRIVER" value="array"/>
    <server name="TELESCOPE_ENABLED" value="false"/>
</php>

This tells laravel that we want to use an SQLite in-memory database when testing.

Writing our tests first

Notice that till this point we haven’t written any line of code, it is a fresh laravel installation.

We will start with the tests and let them guide us to the implementation. we will be using the file tests/Feature/ExampleTest.php to keep things simple.

Before we start writing our tests, we need to add a trait to our test class to refresh the database before the test is run, so add this inside your test class:

+ use Illuminate\Foundation\Testing\RefreshDatabase;

class ExampleTest extends TestCase
{
+     use RefreshDatabase;
}

Inside the method test_example , We want to post data to the route /register and we expect that a new user is created with the data we provided, as simple as that.

This is our test and we will go through it line by line

/**
 * A basic test example.
 *
 * @return void
 */
public function test_example()
{
    $this->withoutExceptionHandling();
    $data = [
        'name' => 'John Doe',
        'email' => 'john@example.com',
        'password' => '11111111',
    ];

    $response = $this->post('/register', $data);

    $response->assertStatus(200);
    $this->assertDatabaseHas('users', [
        'name' => $data['name'],
        'email' => $data['email'],
    ]);
}

Because we want to see all the errors we get we will start by disabling exception handling ($this->withoutExceptionHandling();).

Next, we will prepare our user data and we post this data to the url /register , at this point we have done the work necessary to register a user, next we want to know if our request was successful and if the user is created in our database.

$response->assertStatus(200); this will make sure that we get a 200 status code back from the server as a response status code.

Now to the most important check, we need to make sure that the user is actually present in our database, for this laravel provides a helpful method assertDatabaseHas to check if a specific table has the value we expect to be present. In this case, we expect the database to have our user information and the hashed password.

This is it. now we are ready to start implementing the user registration feature.

To run the test use the command vendor/bin/phpunit in your terminal. If we run the test we will get an error that says something like this:

Symfony\Component\HttpKernel\Exception\NotFoundHttpException: POST http://localhost:8000/register

At this point, we know that the test will fail because we don’t have a /register route to handle the registration logic. So this is our next step.

In routes/web.php file we will add a new route to handle the registration logic, for the sake of simplicity we will not worry about validating the user input (It is a best practice to always validate user input)

Route::post('/register', function () {

});

Now if we run the test again we will see the error:

Failed asserting that a row in the table [users] matches the attributes {
    "name": "John Doe",
    "email": "john@example.com"
}.

The table is empty..

Which is expected because we are not doing anything inside our route handler. So let's accept the user data and register the user.

use App\Models\User;

Route::post('/register', function () {
    User::create([
        'name' => request('name'),
        'email' => request('email'),
        'password' => bcrypt(request('password')),
    ]);
});

Now if we run the test it will pass and we will be confident that our registration endpoint is functioning as expected!

This is a very simplified test but hopefully, with this knowledge, you can start testing your laravel apps.

To learn more about testing laravel apps, you can consult The laravel docs


If you liked this post consider sharing it :

You may also like

stopwatch3 MINS 
cover
By Mohammed Omer | 7th Dec 2021
#laravel #linux
stopwatch4 MINS 
cover
By Mohammed Omer | 1st Dec 2021
#laravel #security #testing
stopwatch2 MINS 
cover
By Mohammed Omer | 27th Mar 2022
#laravel #productivity #tooling #shorts 🔥
stopwatch4 MINS 
cover
By Mohammed Omer | 21st Dec 2021
#laravel #performance
stopwatch14 MINS 
cover
By Mohammed Omer | 22nd Jan 2022
#laravel #linux #nginx
stopwatch6 MINS 
cover
By Mohammed Omer | 15th Mar 2022
#laravel #tooling #php