As we saw in the previous post of this series, we want our tests to provide us with fast feedback about the correct behavior of our application.
In this post, we will touch an aspect of the testing process not usually written about: how to work faster with tests. We will talk about three things you can do to make your testing flow more efficient.
Write them faster!
The book The Pragmatic Programmer, despite being 20 years old, is full of good advice, much of which still applies to modern software development.
One of its tips is this:
Use a Single Editor Well
Modern IDEs have ways to automatize almost everything, and investing some time in knowing the nuts and bolts of the tools you use for most of your coding time absolutely pays off. When it comes to tests, the sooner you get them written the sooner you can start to get feedback from them. What can you do to write them faster?
As an IntelliJ user and PHP programmer, I use PHPStorm as my IDE. Below I describe some functionalities that improve the speed of writing tests substantially. There is likely something similar in whatever tool you use (unless you go full Windows' Notepad, in which case you can skip this whole section).
IntelliJ's IDEs have a templating system for almost all the code they autogenerate for you. When it comes to tests we can, for instance, create them with a setup method setting a new SUT (System Under Test) instance as a property and maybe extending a base test class for the concrete project. Just analyze what you always do manually when creating a test and incorporate it into the test templates.
Then we have the keyboard shortcuts that allow us to navigate between a class and its test(s). If the class has no tests, it will show an option for creating one (which will make use of the aforementioned templates).
Here is a working example of the templating system and keyboard shortcuts in action (well, static action). First the template:
<?php
#if (${NAMESPACE})
namespace ${NAMESPACE};
#end
#if (${TESTED_NAME} && ${NAMESPACE} && !${TESTED_NAMESPACE})
use ${TESTED_NAME};
#elseif (${TESTED_NAME} && ${TESTED_NAMESPACE} && ${NAMESPACE} != ${TESTED_NAMESPACE})
use ${TESTED_NAMESPACE}\\${TESTED_NAME};
#end
use PHPUnit\Framework\TestCase;
class ${NAME} extends TestCase
{
/** @var ${TESTED_NAME} **/
private $sut;
protected function setUp()
{
parent::setUp();
$this->sut = new ${TESTED_NAME};
}
public function testSomething(): void
{
//TODO
}
}
When, in the body of a class, we press the shortcut for jumping to test, the IDE shows an option to go to one of the available ones or create a new one:
Choosing "Create New Test" will use the template and bring up the following automatically:
<?php
namespace Example;
use PHPUnit\Framework\TestCase;
final class MyClassTest extends TestCase
{
/** @var MyClass * */
private $sut;
protected function setUp()
{
parent::setUp();
$this->sut = new MyClass;
}
public function testSomething(): void
{
//TODO
}
}
Now, in the test class, using the jump to test shortcut again brings us we will be back in the tested class.
Get in the habit of using these shortcuts and tweak the templates to your needs and you will gain considerable speed when writing tests.
Run them faster!
The less time you invest from the moment your mind thinks I want to run the tests now and the tests start to run, the better. If we somehow automate the process of running tests in some mechanical actions we will do it seamlessly in our coding workflow without losing focus.
If you use IntelliJ IDEs, it is worth knowing about keyboard shortcuts to:
run the tests under the current scope—A single test if cursor is inside a test method or the whole test case if you are in the class but outside a method.
rerun your last run, which is especially relevant when doing TDD.
Also, if you are not familiar with them, run configurations are also an important time saver. You can define them for only unit tests, for only integration tests, or for a concrete suite or folder and then use a keyboard shortcut to run them automatically.
If you are not an IntelliJ user, consider spending some reasonable time in writing scripts that will reduce the time to run your tests suites.
In the PHP world, you have the option to configure Composer (the standard package management tool) scripts to run arbitrary stuff. For instance, this is part of the script section of one of my project's composer.json file:
"scripts": {
"test-unit": "phpunit --configuration phpunit.xml.dist --testsuite unit",
"test-integration": "phpunit --configuration phpunit.xml.dist --testsuite integration",
"test-functional": "phpunit --configuration phpunit.xml.dist --testsuite functional",
"test-all": "phpunit --configuration phpunit.xml.dist",
"all-checks": [
"parallel-lint . --exclude vendor --exclude var",
"phpcs -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests",
"phpunit --configuration phpunit.xml.dist",
"vendor/bin/phpstan analyse"
]
},
So, running a terminal composer test-unit
will run all my unit tests. It is the same for other suites. And there is this handy composer all-checks
that will run linters, tests, and PHPStan, all in one.
Run them often!
So, now you can implement your tests in a breeze and run them with a subsecond, lightning-fast hand movement. It would be a waste not to use this superpower, so try introducing the act of running tests in your coding flow as something natural. This is a must to get these so desirable fast feedback loops.
If you do TDD (and you should), it is a core part of the process. If not, train yourself. Even more, given we trend to forget stuff, might it be a good idea to somehow enforce running our tests with every commit we do.
In the IntelliJ world you can configure scripts to run before doing a commit. This implies using git through the IDE's GUI.
Another option is to configure your project git hooks to run the tests before a commit, canceling it if they are not green.
Or even aliasing shell commands, so you just do not commit even more but test-and-commit always.
Tools like GrumpPHP, obviously in the PHP ecosystem, help for those automations.
Do not stop with tests!
All the previous advice focuses on tests, but for sure your spider powers have already been activated and you noticed these tips apply to the whole coding process.
One thing we programmers do is automatize solutions to problems. Investing time in doing so for tasks we do over and over every day seems like a good move.
Summarizing
We have seen three ways to speed up writing and using tests:
- Writing them faster.
- Running them faster.
- Running them often.
These three ways can be automatized with the technology we use on a daily basis. So, give it a try!
Further reading
- This article talks about optimizing what you do most of time, not what you do not do.
- As usual, is not a good idea to overengineer. Listen to XKCD and think about the effort vs the value.
Top comments (2)
Nice article 👍 👍 thanks!
Glad you liked it!