|

Laravel Testing Helpers

Laravel Testing Helpers

We’ve talked a little bit about helper functions in Laravel before. In this tutorial, let’s take a look at creating some helper functions for our testing classes. Essentially, all we want to do is take some of the repetitive code we see in in our tests, and wrap up those snippets into some functions we can easily reference to help clean things up a bit. We’ll make it so that we will have less typing, and easier test class creation using a combination of helper functions and live templates in PHP Storm. Let’s have a look at how we can do this now.


Adding Tests For Creating Threads

Before we start our refactoring, let’s go ahead and add a test for creating new threads.

vagrant@homestead:~/Code/forumio$ php artisan make:test CreateThreadsTest
Test created successfully.

We quickly stub out our first test and call it test_a_logged_in_user_can_create_new_threads(). Just like all of our other tests, we first have to ask ourselves, what are we trying to test and how can we do this? Well, the pseudocode may look like this.

  • Given there is a logged in user
  • When the endpoint to create a new thread is visited
  • Then the thread page is viewed
  • There should be a new thread

Run the test to see where we are at.

vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 687 ms, Memory: 6.00MB

There was 1 error:

1) Tests\Feature\CreateThreadsTest::test_a_logged_in_user_can_create_new_threads
Illuminate\Database\QueryException: SQLSTATE[HY000]: General error: 1 no such table: users (SQL: insert into "users" ("name", "email", "password", "remember_token", "updated_at", "created_at") values (Arnoldo Howe, kale01@example.net, $2y$10$LT7lrCFwUNExvHIcu7gKeOBxr61R.TlWyIVzpdjUITPuh5qOHmiEK, iA7enRtnGP, 2017-12-19 15:29:30, 2017-12-19 15:29:30))

This error tells us that we are missing the migrations trait that we have been using so far. Let’s include that in our test file.

We can run the test again and see how it goes.

vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 1.16 seconds, Memory: 8.00MB

There was 1 error:

1) Tests\Feature\CreateThreadsTest::test_a_logged_in_user_can_create_new_threads
Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException:

This error indicates we have a missing route. This is an easy one to fix, let’s go ahead and add the route we need.

Of course we’ll also need to fill out the store method on our ThreadsController so let’s tackle that here.

Cool. Let’s run our test and see how it works.

vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 1 second, Memory: 8.00MB

There was 1 error:

1) Tests\Feature\CreateThreadsTest::test_a_logged_in_user_can_create_new_threads
Error: Call to undefined method Tests\Feature\CreateThreadsTest::assertSee()

/home/vagrant/Code/forumio/tests/Feature/CreateThreadsTest.php:28

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.

Hmmm. Call to undefined method assertSee(). What went wrong? Oh yes, we need to fix our test like so:

Running our test now gives us a passing result. Nice!

vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 967 ms, Memory: 8.00MB

OK (1 test, 1 assertion)

Test To Make Sure Guests Can Not Post Threads

In the first section, we have a nice little test that covers the scenario of a logged in user posting a new thread. Our test is working, so all is good. We should also create a test where we prove out that a guest user can not post a new thread. How might we do this? Let’s see.

Running this this test class now gives us one passing and one failing test.

vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 967 ms, Memory: 8.00MB

OK (1 test, 1 assertion)
vagrant@homestead:~/Code/forumio$ clear
vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.

E.                                                                  2 / 2 (100%)

Time: 1.12 seconds, Memory: 10.00MB

There was 1 error:

1) Tests\Feature\CreateThreadsTest::test_guest_can_not_create_threads
Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: threads.user_id (SQL: insert into "threads" ("user_id", "title", "body", "updated_at", "created_at") values (, Nam similique doloremque voluptatem laborum aut qui., Ipsam amet placeat repellat assumenda sit tempore. Doloribus quis ut placeat quod., 2017-12-19 16:12:48, 2017-12-19 16:12:48))

So it looks like it is still trying to create the thread. We reached the method and it is trying to build up the SQL query, but we’re missing a user id. Well, we actually don’t have a user id because this is a guest in this instance. We can fix this with our good buddy Middleware.

Run the test again, and we are getting an unauthenticated error.

vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.

E.                                                                  2 / 2 (100%)

Time: 1.06 seconds, Memory: 10.00MB

There was 1 error:

1) Tests\Feature\CreateThreadsTest::test_guest_can_not_create_threads
Illuminate\Auth\AuthenticationException: Unauthenticated.

Ok, makes sense. This is actually what we want. We can modify our test to actually expect this exception, and then our test should pass.

Bingo! Our test now passes.

vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 1.07 seconds, Memory: 10.00MB

OK (2 tests, 2 assertions)

Creating Test Helpers

Now that we have those tests added, we can clean some things up with helpers. So let’s update our composer.json file to support this like so:

    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        },
        "files": ["tests/utilities/functions.php"]
    },

Now we can create that directory and file as well:
laravel test helpers

Let’s populate that functions.php file like so:

With these helpers, we can now swap out some of the code in our test files with more simplified versions. For example factory('App\Thread')->make() can now be replaced with make('App\Thread'). In addition, factory('App\User')->create() can be replaced with create('App\User').

We must make sure to run a composer dump command with the new helper file in place, otherwise we may see an error such as this:

vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.

F.                                                                  2 / 2 (100%)

Time: 1.42 seconds, Memory: 10.00MB

There was 1 failure:

1) Tests\Feature\CreateThreadsTest::test_guest_can_not_create_threads
Failed asserting that exception of type "Error" matches expected exception "Illuminate\Auth\AuthenticationException". Message was: "Call to undefined function Tests\Feature\make()" at
/home/vagrant/Code/forumio/tests/Feature/CreateThreadsTest.php:18
.

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.

Once we run that composer dump however to rebuild the autoloading files, we are good to go as we see here:

vagrant@homestead:~/Code/forumio$ composer dump
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Package manifest generated successfully.
vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.4 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 1.14 seconds, Memory: 10.00MB

OK (2 tests, 2 assertions)
vagrant@homestead:~/Code/forumio$

Creating a signIn() Helper

In the section above we made nice little shortcut functions for creating model factories. This snippet will make it much easier for creating a signed in user for our tests. We can refactor the TestCase.php file to support this like so:

Once again, we can clean up our test files. With this code, we can swap out instances of $this->actingAs(create('App\User')) with $this->signIn().


Creating A Live Template In PHP Storm

One more thing we can do to help better streamline our testing is to set up a live template in PHP Storm to automatically create a test class based on our needs. Let’s see if we can make this work. First, let’s just create a quick test stub we can use.

vagrant@homestead:~/Code/forumio$ php artisan make:test FooTest
Test created successfully.

Now, go ahead and take the contents of FooTest.php and copy / paste it into PHP Storm in live templates.
php storm add live template

Once you click on Live Template to add the live template, we must fill out abbreviation, description, and paste in the code for our template.
filling out abbreviation and description of live templates

Make sure to define the applicable contexts as well as reformat according to style options. For our purposes we will choose “Everywhere” for the context definition. Now, if we have a blank file, we should be able to type testclass and then hit the tab key to populate some testing boilerplate.

live template in action


Automatically detect file names

Our live template is pretty cool right now, but let’s make it better. We want the live template to automatically name the class based on the file name we have. So if we have a file named BarTest.php, and then type testclass tab in the editor, it will populate the boilerplate based on the name of the file. In the live template settings of PHP Storm, we can change the class name of our markup from FooTest to $CLASS$. Then we choose to “edit variables” and update the class definition to use file name without extension.
phpstorm live template edit variables

Now we can create a new file, and then type out testclass then tab key to auto populate everything for us.
create new test file
auto name class from file name


Laravel Testing Helpers Summary

This was a nice little tutorial that had us building out a few tests for creating threads, followed by some helpful ways to refactor testing code to make it easier to write our test cases. We first made a couple of test helpers in the form of functions that we autoload in development mode only. These functions allow us to use a nice shorthand way to make or create users and or threads. After this, we took a dive into live templates in PHP Storm. We were able to set up a nice live template that uses the base file name as the class name when we create a new test. When typing our trigger word, in our case testclass, followed by the tab key – we get the auto population of some helpful boilerplate code. These steps are going to make it easier and faster to keep writing tests as we move forward.

|