Laravel Logo

Why It Exists

The make:test command creates a unit test class so you can test your application.

When Should You Use It

All the time when you’re doing Test Driven Development (TDD).

Creating a new controller? Run this first.

Creating a new model? Run this first.

Feature Tests

One of the things this command does that is super nice is that it distinguishes between tests that are used to test an endpoint (feature tests) and specific functions (unit tests). I highly recommend you setup a feature test for each controller before you create the controller so you can test each route as you add it to the controller. This will give you a quick way to make sure you didn’t break anything inadvertently. These tests are slower so you won’t want to run them all the time but if you have a continuous integration server it can run them for you.

Feature tests are stored in the “tests/Feature/”” directory.

For our examples, we’re going to create a Project module. To create our test class to test our ProjectController we can run the following command.

ubuntu@ubuntu-xenial:/var/www$ php artisan make:test ProjectControllerTest
Test created successfully.

This creates the following class:

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class ProjectControllerTest extends TestCase
{
    /**
     * A basic feature test example.
     *
     * @return void
     */
    public function testExample()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

I always change the testExample() function to test the index route:

public function testIndex()
{
    $response = $this->get('/projects');

    $response->assertStatus(200);
}

Now we can run the test and get the following results:

> Executing task: /usr/bin/ssh -tt -p22 ubuntu@192.168.56.101 "/var/www/vendor/bin/phpunit /var/www/tests/Feature/ProjectControllerTest.php --filter '^.*::testIndex( .*)?$'" <

PHPUnit 8.4.1 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 1.44 seconds, Memory: 16.00 MB

There was 1 failure:

1) Tests\Feature\ProjectControllerTest::testIndex
Expected status code 200 but received 404.
Failed asserting that false is true.

/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:166
/var/www/tests/Feature/ProjectControllerTest.php:20

Unit Tests

Unit tests are used to test specific functions and not a group of classes. They need to be quick to run so you can get quick feedback. 1/10th of a second per test should be your goal.

Unit tests are stored in the “tests/Unit/”” directory.

To create a unit test instead of a feature test you need to include the --unit parameter when creating the class.

ubuntu@ubuntu-xenial:/var/www$ php artisan make:test --unit ProjectTest
Test created successfully.

This will create the following test.

<?php

namespace Tests\Unit;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class ProjectTest extends TestCase
{
    /**
     * A basic unit test example.
     *
     * @return void
     */
    public function testExample()
    {
        $this->assertTrue(true);
    }
}

Helper Functions

Laravel has some very helpful functions and facades to make testing easier. This section will discuss a couple of them.

assertDatabaseHas()

assertDatabaseHas() is used to run a database query against a table to see if a set of conditions have been met.

$this->assertDatabaseHas('database_table_name', [
    'condition1' => $value1,
]);

One of the things I like to do is to create a “random” name that I can then query to see if my actions have been successful.

$randomName = 'testCanSubmitWithCorrectData' . rand(0, 999999);

// do thing to create project

$this->assertDatabaseHas('projects', [
    'name' => $randomName,
]);

assertDatabaseMissing()

assertDatabaseMissing() in the inverse of assertDatabaseHas() and it’s used to run a database query against a table to see if a set of conditions have not been met.

$this->assertDatabaseMissing('database_table_name', [
    'condition1' => $value1,
]);

I like to start the test above with a call to assertDatabaseMissing() to make sure I’m not testing for something that’s already in the database.

$randomName = 'testCanSubmitWithCorrectData' . rand(0, 999999);

$this->assertDatabaseMissing('projects', [
    'name' => $randomName,
]);

// do thing to create project

$this->assertDatabaseHas('projects', [
    'name' => $randomName,
]);

factory()

The factory() facade is used to generate an instance of a class using it’s factory class:

$user = factory(\App\User::class)->create();

Our next post in this series will discuss factory classes more so subscribe if you want to know more about them.

Helper Functions in Feature Tests

get()

The get() function is used to run an HTTP GET request against your application. You can then use assertStatus() to check the return code.

$response = $this->get('/');
$response->assertStatus(200);

$response->assertStatus(200); can also be replaced by $response->assertOk();. I like how “Ok” is a little more expressive and you don’t need to remember 200 is the “correct” response code.

assertSeeText()

The assertSeeText() function can be used to check if a response has some specific text.

$response = $this->get('/');
$response->assertSeeText('Projects');

post()

The post() function is use to run an HTTP POST request against your application. The array passed as the second parameter contains all the parameters you would normally submit. The withoutMiddleware() function is used below to make sure we can post the values without having to worry about the CSRF token being missing. I also added a check to assertRedirect() because we would expected to be redirect to a page that would display our results.

$this->withoutMiddleware();
$response = $this->post('/projects', [
    'name' => 'name',
]);

// check that the project was created using `assertDatabaseHas()`

$response->assertRedirect();

actingAs()

The actingAs() function allows you to set the currently logged in user so you can request authorization required routes:

$user = factory(\App\User::class)->create();
$this->actingAs($user);
$response = $this->get('/secureuri');
$response->assertOk();

Hopefully this has been as helpful to you as it has to me and check back soon for more artisan commands.