Writing tests for Laravel applications is really easy but like all things with software development it can become bloated, unwieldy and just all around difficult to navigate over time. I’ve found that you often have to go back and review what you’re doing with your application’s automated tests so that they stay useful, reliable and readable. Along the way these are a few of the steps I’ve gone through to improve my test suites and my overall strategy for testing.
One of the first things I suggest is if you find yourself with a lots of Feature tests, try breaking them up into different suites. PHPUnit already supports this kind of structuring and Laravel uses it to break the tests up into Units and Features. The problem is, Feature tests can be quite slow so when you’re developing you might end up running all the feature tests regularly when really all you care about are particular groupings of test.
For example, say if I have a project that isn’t just a customer facing website, it also has functionality to serve administrators at the company and to server external business clients.
The logical thing would be to then break down the feature tests into 3 new categories, Web, Admin and Clients. This way, when I work on a client facing feature I can test the client suite first because this is where I’m more likely to see a regression in the application, once that has passed I’ll run the other two suites to make sure there is no unexpected knock on effects. It’s also good to run the groups separately on your CI runs, that way it’s easier to see where things went wrong or how to split tests into parallel builds.
This is very subjective and if I’m honest I don’t know if there’s an exact science to which is best and when. You often just have to feel your way through. I mentioned feature and unit tests in the last section and both are very useful for different cases but ultimately feature tests are often going to be a lot slower than unit tests.
Generally when you write a unit test, you’re checking the API of the actual classes and functions you’ve developed, as if you were making a library to give to another developer. You check things like given a method receives arguments A and argument B, that method should return C. You’re really just looking to test the code here as a reusable component. This is generally going to be faster as it’s not relying on making HTTP requests to the application or seeding bits of data into the database.
An example of this is for example is a custom validation rule. While you often use validation rules on a feature level to validate a form or some other input, it isn’t very productive to run that test multiple times just to establish all the values that are and aren’t valid for a particular rule. At best you might try a few edge cases. Instead try writing unit tests which bypass all the form handling stuff and instead just tests the rule in isolation to check all the different values that may or may not pass.
I wrote about doing this some time back but the article itself should still be relevant today.
There’s a few things that can be resolved here. If you find yourself doing the same things in your test a lot of the time, it’s a good sign that you need to do some housekeeping. There’s a few different things you can do here which are cover in a few different places.
The first is traits which I wrote about in another tip. Essentially when you want to setup up the same logic is really helps to consolidate a lot of those things into multiple traits you can throw into the test case.
The next is factories. Factories help you produce the data for a feature test in easy ways but they can get quite complex. Soon you have long tests with lots of set up. What’s worse in when you’ve got several tests to run all doing the same thing. Tighten has done a really good article explaining how you should start to make dedicated classes for this and I think this is personally a great way of reducing duplicated code for your tests.
The final step is macros. Again I have to point to a previous article of mine which sets things out how you can use macros with classes like the TestResponse class to reduce a lot of the same things you assert against.
The beauty in unit tests is that they should always be really fast if you’re sticking to the principles of not touching databases or any external services during as part of your unit tests. Often though you will have made a Unit test using the php artisan make:test --unit SomeTest which will extend the TestCase in your tests folder. If you want to give your unit tests a boost and they don’t require use of the Laravel framework’s service container you can shave a good amount of time off your tests.
To do this all we need do is remove the reference to extending Tests\TestCaseand instead use the base TestCase PHPUnit\Framework\TestCase . If your test doesn’t require the Laravel framework, it’ll all run the same but it’ll be the faster for not building the Application state before each test. The gains will seem trivial on a small set of tests but can make serious gains for large enough test suites.
In fact I first noticed this on a test where it was taking 3.89 seconds to complete the whole class and then by switching to using the PHPUnit test case it was now only taking 154ms for the test case to complete. You can often find ways of fixing this further by refactoring tests away from using the Laravel components and do some mocking instead if needed.
PHPUnit has a thing called Data Providers. A data provider is essentially where a test can be run multiple times but with parameters to the test function. This reduces a lot of code you’ll write and it makes adding new scenarios a lot quicker.
Writing a data provider is as simple as the following:
As you can see from the example it’s now going to run through the two scenarios for the Sums test using the variables provided by the sums method. The more you practise using this technique the more you’ll find just how useful it can be.
This is a fairly well known trick for anyone developing packages or has divided into using PHPUnit enough to know. The best practise with PHPUnit is to actually set your phpunit.xml file to be ignored by any source control you use. Instead you should include a phpunit.xml.dist file in your project to act as the default template. Then, should you want to customise how you run PHPUnit on your current setup you can copy phpunit.xml.dist to phpunit.xml and edit the file without those changes being sent to other developers via source control.
A common reason for doing this is you might want to remove any code coverage for when you’re running tests. Instead keeping that in your phpunit.xml.dist file and simply calling phpunit --conf phpunit.xml.dist when you do want to generate code coverage data.
There’s always something more you can do for your testing strategy. Not all of these points might be useful to you but I’m sure at least one of them will be if you’re not doing that already. If you have any tips of your own please comment because I’d love to know how you manage your tests over time. Thank you for reading.
I’m Peter Fox, a software developer in the UK who works with Laravel among other things. If you want to know more about me you can at https://www.peterfox.me and feel free to follow me @SlyFireFox on twitter for more Laravel tips and tutorials.