Laravel 7 Application Testing: Let's Start With Something Small
Updated on September 24, 2020
TweetI have been working on a side project for quite some time now. The application is coming out really well, at least I believe so. To make sure that I am not coding a hot pile of garbage, I think some unit and feature testing is in order.
Setting The Scene
Since I have not yet released a beta version of the application, I put up a Request Early Access form. This is a standard form that takes: First Name, Last Name, Company, Email and Phone.
I created a custom form request class to handle the validation rules and brought that into my invokable class:
This is all standard Laravel fare. But because it is pretty standard, we should be able to easily create some tests around it. The first test we are going to try, is to test what happens when you submit the form with nothing. If a user lands on my page and just clicks Submit, a validation error should be returned. Let's create this test now:
php artisan make:test Http/Controllers/RequestAccessTest
A couple things to consider with the above command, when creating a feature test, it is apparently standard practice to mirror your App folder. This way, it is easy for you to identify what controllers are being tested in your application. The second thing, is that by default, the command creates a feature test. If you wanted to create a unit test, you need to pass the unit flag into the command:
php artisan make:test RequestAccessTest --unit
For now, we are going to focus on the feature test, so let's move on.
A Test Is Born
Let's take a look at the default test that Laravel created for us.
Doesn't look like anything crazy. It looks like it just hits the root URL and expects a 200 response. Before we make any changes to this default template, let's just run it. Artisan makes this super easy:
php artisan test
This gave me the following result:
Exactly what we expected, very cool. For this specific test, we don't need to test the root URL, so we can just remove that and start creating the test where the form is submitted with nothing.
I have officially written my first, real feature test. It is really satisfying to run the tests and see it pass. You gotta love the little things in life that can make you smile. This may be a small test, but it is a big step forward for me as a developer! So let's break this down a bit.
$this->post('/request-access', []);
Instead of doing a get, like in the default test that was created, we are going to do a post. The first parameter is where you will define the URL that we are going to post to. The second parameter will be the request data.
Next we are making sure that the validation fails by asserting that the status is going to be a 302. This specific test is testing if the form is submitted with none of the three required fields: first_name, last_name, email. Using the assertSessionHasErrors function, we can pass in a single value we are testing against or an array of values.
When we run this puppy, this is what we get:
Woot woot! We did it! Also, check out how PHPUnit is displaying my new test. It took my function name of testReturnValidationErrorWhenEmptySubmission and turned it into a readable sentence. Because of this behavior, it is a good idea to be very descriptive of what you expect the result of your test to be. When you have a long list of tests running, you want to be able to quickly identify what specifically failed.
The First Step Is The Hardest
Now that I finally have my first test in the bag, I feel a lot more confident about creating tests, but more importantly, understanding why it is necessary to build tests around your features. As I eventually start expanding the code base and making large architectural changes, I need to make sure that I am not affecting a critical function else where in the code base. By making a habit of creating tests and running them often, I will make sure that I don't futz around with a pre-existing function.
And once I get my github actions setup, I will be able to have github run the tests and let me know if something fails BEFORE I merge bad code into the master branch. That is a huge deal!