Mix
comes with a good set of tools, but it’s true power can be seen when we
try to configure it. Mix
makes it awfully easy to provide some basic
configurations and overrides. One such configuration is aliases
.
In this post, we’re going to use aliases
to make our TDD lives easier while
using a mix
project.
$ mix test
is the main task which runs all the tests for a mix project. It takes
a command line option, --stale
which runs only the tests which references the
modules that changed since last $ mix test --stale
call. You can read more
about it here.
It also accepts the option, --listen-on-stdin
which listens on stdin while
running the tests. When running tests with this option, tests are run again when
it receives a newline. We can use these options in combination to give us the
ability to run stale tests with just one keystroke: $ mix test --stale --listen-on-stdin
.
Now, we want to integrate this with our file system, such that whenever we change “relevant” files, stale tests get run automatically without even a single keystroke.
To accomplish this we can write a custom alias in the mix.exs
file:
This example is for an umbrella app, but can be modified for a non-umbrella too.
# in mix.exs
defmodule MyUmbrella.Mixfile do
use Mix.Project
def project do
[
apps_path: "apps",
start_permanent: Mix.env() == :prod,
deps: deps(),
aliases: aliases()
]
end
defp deps do
[]
end
defp aliases do
[
"test.watch.stale": &test_watch_stale/1,
]
end
# This function runs a system command with a watcher depending upon the
# Operating System.
def test_watch_stale(_) do
System.cmd(
"sh",
["-c", "#{get_system_watcher()} apps/ | mix test --stale --listen-on-stdin"],
into: IO.stream(:stdio, :line)
)
end
# Works only for Mac and Linux
defp get_system_watcher do
case System.cmd("uname", []) do
{"Linux\n", 0} -> "inotifywait -e modify -e create -e delete -mr" # For Linux systems inotify should work
{"Darwin\n", 0} -> "fswatch" # For Macs, fswatch comes directly installed
{kernel, 0} -> raise "Watcher not supported on kernel: #{kernel}"
end
end
end
By defining a custom alias test.watch.stale
and delegating it to the function,
test_watch_stale/1
, we are adding the ability to call $ mix test.watch.stale
from the command line which calls the function test_watch_stale/1
with the list
of arguments we pass from the command line (in this case none, so an empty list).
test_watch_stale
function calls mix test --stale --listen-on-stdin
piped
from a watcher function which spits an stdin whenever an event is triggered,
which in this case is when apps/
folder is modified.
get_system_watcher
function gets the system watcher commands for different
operating systems. Macs come preinstalled with fswatch
, so this should work
on a mac right away. Linux systems will have to install inotify-tools
for this
to work.
I decided not to support Windows for now, but there are file watcher binaries like watchman that work on all OS.
Now, we can follow the perfect TDD where our changes to the file system trigger relevant tests.