If you just want a tl:dr, here’s the script you can plop in whatever .bashrc
file you’re using—explanation follows.
The pw
function runs your tests once by calling phpunit tests
, watches every php file in src
and tests
, and runs the tests again when a watched file changes.
You can specify any PHPUnit arguments after pw
, e.g. pw ./tests/Unit/FooTest.php
or pw --filter test_true_is_true
.
The script uses Facebook’s Watchman library, which—on OSX—can be installed installed via Homebrew.
brew install watchman
Watchman watches files and triggers actions when they change. The reasoning behing choosing Watchman: it’s easy to install, simple to configure, and reliable.
The watchman-make
command—which ships with Watchman—is a specialised interface for Watchman to invoke build tools in response to file changes—exactly what we need!
Let’s do a line-by-line review of our watch function.
function pw { }
The function name determines the command name. I like short commands—PHPUnit is aliased to p
on my machine—so an abbreviated version of phpunit-watch
seems like a good fit.
run="clear && printf '\e[3J' && vendor/bin/phpunit"
Since we’re going to need the actual “run” command twice, let’s store it in a variable. To break it down further, clear && printf ‘\e[3J’
clears the terminal (to keep previous test runs from cluttering it) and vendor/bin/phpunit
runs the tests.
[[ -n $@ ]] && args=$@ || args="tests"
watchman-make
needs arguments to work. (I’d love to be proven wrong here so I can clean this part up!) We’ll default the arguments to tests, which means the actual command that we’ll run is vendor/bin/phpunit tests
. If we provide any arguments to pw
, they’ll replace test
, for example pw —-stop-on-failure
would run vendor/bin/phpunit —-stop-on-failure
.
The next two commands bring everything together.
eval "$run $args"
Manually triggers the command once before watching. This way we see immediately see test results without having to change a file first.
watchman-make \-p 'src/**/*.php' 'tests/**/*.php' \--make=$run \-t "$args"
Finally, time for the Watchman part! The -p
parameter specifies which folders we want to watch. I personally set up way more globs like app/**/*.php
and database/**/*.php
since I’m mostly working with Laravel. --make
specifies which command we’re going to run on change, and -t
will pass extra arguments to the--make
command (remember, that variable we defaulted to tests
).
I’m still dreaming of a Jest-like CLI tool for PHPUnit, which also allows you to filter and rerun specific tests without breaking out of the watch function, but being able to run tests on change is already a vast workflow boost.
To wrap things up, here’s the full pw
function again:
This post builds further on a Coffee and Code article by Jonathan Knapp.