This post is quite old now.
If it's a technical article, this means that some of the information in this post might be out of date.
Recently I've started to write tests in my Laravel applications. Tests are useful because they allow you to make sure important parts of your application are working, things like registration or subscriptions. Things that could be easily missed and could cause uproar if they broke.
When I started building ReadCast, about a month ago I created it as a prototype and missed out on writing tests. Now that the application is stable and has a few users, I think it's about time to write some tests.
In a standard Laravel install, PHPUnit is already installed for you and so are a few example tests.
In this tutorial I'm going to go through:
Setting up your tests
Creating database seeds
Writing tests
Setting up your tests
In Laravel, your tests belong inside the tests directory. In there you have two directories. One for your feature tests and one for your unit tests. I should probably know the difference between this but I don't, so go elsewhere if you want to know the meaning.
Most applications will talk to a database when doing anything. When testing, there are two types of databases that people will likely use. They will either use a SQLite database or a MySQL database.
If you don't know, a SQLite database is pretty much a lightweight SQL database that's stored in a single file.
I tend to create a separate MySQL database for running my tests. Why? Because last time I tried to use SQLite I managed to break some dependency that every programming language needed which meant I had to rebuild my Mac, not a fun time. However, feel free to use whatever you like best.
If you like using MySQL like me, this is how I tend to configure it.
First, I create my testing database, usually called something like app_testing.
The next thing I do is go to my database.php configuration file and setup a new testing database connection. It looks like this:
<?php 'testing' => [ 'driver' => 'mysql', 'host' => env('TESTING_DB_HOST', 'localhost'), 'database' => env('TESTING_DB_DATABASE', 'app_testing'), 'username' => env('TESTING_DB_USERNAME', 'homestead'), 'password' => env('TESTING_DB_PASSWORD', 'secret'), 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', 'strict' => false,]
The values rely on some environment variables so you'll need to add these env variables to your .env and .env.example files.
TESTING_DB_HOST=TESTING_DB_DATABASE=TESTING_DB_USERNAME=TESTING_DB_PASSWORD=
Now there's one more thing you need to do before being fully setup. You'll need to specific the type of database you wish to use when testing. We can do this in PHPUnit's configuration file, phpunit.xml.
In the bottom <php> part, make it look like below, where we add the DB_CONNECTION rule and specify the testing database connection we just created.
<php> <env name="APP_ENV" value="testing"/> <env name="BCRYPT_ROUNDS" value="4"/> <env name="CACHE_DRIVER" value="array"/> <env name="MAIL_DRIVER" value="array"/> <env name="QUEUE_CONNECTION" value="sync"/> <env name="SESSION_DRIVER" value="array"/> <env name="DB_CONNECTION" value="testing"/></php>
That should be us done with the setup.
Setting up factories
Factories allow you to you to generate database records. Laravel comes with a package called Faker which makes it easy to create factories with fake information. Things like names, emails, passwords, that sort of thing.
You can find your factories in the database/factories directory. If you need to create your own factory, you can do so with a simple artisan command: php artisan make:factory ArticleFactory
Laravel comes with a User Factory, so we'll just use that one. The user factory looks like this:
<?php use Illuminate\Support\Str;use Faker\Generator as Faker; /*|--------------------------------------------------------------------------| Model Factories|--------------------------------------------------------------------------|| This directory should contain each of the model factory definitions for| your application. Factories provide a convenient way to generate new| model instances for testing / seeding your application's database.|*/ $factory->define(App\User::class, function (Faker $faker) { return [ 'name' => $faker->name, 'email' => $faker->unique()->safeEmail, 'email_verified_at' => now(), 'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret 'remember_token' => Str::random(10), ];});
The information in the sample user factory is fine for me but you might want to change it, depending on what fields you have in models.
Writing tests
Laravel gives you a pretty basic test which visits a page and the test passes if the status of the page request is 200 (that means its OK).
<?php namespace Tests\Feature; use Tests\TestCase;use Illuminate\Foundation\Testing\RefreshDatabase; class ExampleTest extends TestCase{ /** * A basic test example. * * @return void */ public function testBasicTest() { $response = $this->get('/'); $response->assertStatus(200); }}
Now that we know what tests look like, let's delete the example test and create our own. To create our example test, run this command:
php artisan make:test LoginTest
In the test I'm going to write, I'm going to create a user, login to my application, submit my email and password then the test will pass if we get status code 200.
Just a heads up, when writing test, make sure to start the method with test so PHPUnit knows which methods are actually tests.
In our test, we need to specify that we want to use the RefreshDatabase trait. This will allow us to have a clean database for every test meaning results won't be messed up with those from other tests, You can add trait like this:
<?php namespace Tests\Feature; use Tests\TestCase;use Illuminate\Foundation\Testing\WithFaker;use Illuminate\Foundation\Testing\RefreshDatabase; class LoginTest extends TestCase{ use RefreshDatabase; //}
Now let's write the test. The first thing I said we would do in the test would be to create the user we want to login. We can use Factories to do this. In the code sample below, we're creating a user and storing it's information in the user variable.
$user = factory(\App\User::class)->create();
Now we have created the user, we need to send a POST request to my login endpoint with my email and password.
The below code will find the input with the name of email and will fill it with the email of the user we created and will find the input with the name passwordand will fill it with the password of the user (by default, Laravel's user factory password is 'secret'). It will then find the button with the text that says 'Login' which submits the form. Then it checks to make sure we're redirected successfully to the /articles url.
$response = $this->post('/login', [ 'email' => $user->email, 'password' => 'secret']); $response->assertStatus(302) ->assertRedirect('/articles');
You should now have something that looks a little like this:
<?php namespace Tests\Feature; use Tests\TestCase;use Illuminate\Foundation\Testing\WithFaker;use Illuminate\Foundation\Testing\RefreshDatabase; class LoginTest extends TestCase{ use RefreshDatabase; public function testLogin() { $user = factory(\App\User::class)->create(); $response = $this->post('/login', [ 'email' => $user->email, 'password' => 'secret' ]); $response->assertStatus(302) ->assertRedirect('/articles'); }}
If you've done everything correctly, you should be able to run your tests and see them all pass.
And that's it, we've got Laravel setup and we've written tests to make sure logins to our application are working.