Automatically run tests when a change occurs
I would like to talk to you about unit tests and how you can automatically run them as soon as you modify code. I regularly use the Test-Driven Development approach when I write code for a project or when doing a code kata. Sometimes I write a little bit too much before running tests again, and when tests turn red, I’m telling myself “Oh! But since when was I wrong?”.
I am jealous of the experience Visual Studio offers with C# and the Live Unit Testing feature. Each line of the code is decorated with a symbol indicating if it is covered by passing tests or failing tests, in real-time. Awesome. I need something like that.
I started by searching a similar tool for the languages I use. Then I created a simple script, which runs a command after each file change. Finally, I found a really nice tool for file change monitoring. It is powerful, cross-platform, and open-source. And that’s what I want to talk to you about.
Watch my changes! 🔍
One way to improve the feedback given by our tests is to know immediately if a change breaks a test. To achieve this goal, we have to continually run tests. The more often we run the tests, the smaller the failing piece of code will be. Ideally, tests should be run after each change. But we can easily forget, or it can be cumbersome.
So we need a way to monitor files and automatically execute a command if there is a change. Some languages and tools offer a built-in way to do that. For example:
- Kotlin through Gradle >
gradle test —-continuous
- Elm with elm-test >
elm-test --watch
But what if the programming language and tool we are using do not have a watch option? This is where fswatch comes in.
fswatch 📡
Open-source and cross-platform, fswatch is a great tool to monitor file change. It provides recursive directory monitoring, filtering, and supports many OS-specific APIs such as kevent, inotify, and FSEvents.
By using these low-level APIs, fswatch is able to detect any changes in your specified paths and to send informations to another command. The tool is good enough to throttle the execution of the command: it will wait for the user to stop editing the code.
For more information about features or how to install it, please take a look at the GitHub repository.
https://github.com/emcrisostomo/fswatch
Usage
In most cases, you simply want to fire a command as soon as a change is detected in a folder. It’s as simple as this line:
fswatch -or YOUR_PATHS | xargs -n1 -I{} YOUR_COMMAND
fswatch accepts a list of paths to monitor. The o
option asks the tool to group changes by batch, and the r
option stands for watching subdirectories recursively. Events are then sent through xargs
to your command. Here is an example for swift, observing one folder:
fswatch -or ./Project | xargs -n1 -I{} swift test
Note that fswatch will run until you stop it, by pressing Control + Z
on macOS or Control + C
on Linux for example. But that’s what we want, run indefinitely regardless of the test results.
Going further 🚀
So, you got the basics. Now we will see how to improve it.
Choose carefully the files to monitor
You may want to not run your tests for every kind of file changed. A change in an image or in a temp folder probably does not interest you. Observing the right files allows you to run tests only if an impacting change occurs. It can also prevent you from going into an infinite loop.
I had a case where build files were generated after each test, so that triggered the watch, and run tests again, and changes appear, and… this story will never end. The easiest solution for me was to specify multiple sub-directories as input to fswatch` instead of the whole project folder. Remember that you can set multiple paths.
fswatch -or ./Sources ./Tests | xargs -n1 -I{} swift test
The other way is to include or exclude paths matching a regex. According to the fswatch manual, you can set options like -i, --include
and -e, --exclude
for that.
Create a script
Sometimes, the command you want to fire for each change is long and requires a lot of parameters. It may even be composed of multiple commands. In that case, I recommend you to decompose this step and to create a script to run your tests.
fswatch -or ./Sources ./Tests | xargs -n1 -I{} run-tests.sh
Better, isn’t it? But we can still improve that. We can avoid typing that long line every time we start working. You see me coming, we’ll make a second script that will do it.
live-tests.sh
Keep it visible
Now that we have a real-time feedback, we want to see it. It would be a shame to have it hidden in the background. Good news, it is quite easy to keep a terminal in the foreground. Perhaps your favorite editor has already a panel with a terminal, and you just have to show it. Visual Studio Code, for instance.
Some editors like Sublime Text do not have a terminal panel by default, but you can add it with a plugin or extension. And if that’s not possible, you still have the option to put the terminal on a second screen, if you have one, or to reduce your editor window and put a terminal at the left, right, or bottom of it.
You have everything you need to get started. Monitoring files is at your hands. Enjoy the continuous and real-time feedback of your unit tests.