Test-driven development (TDD) involves running automated tests. A lot. If you have a function with five tests, you might easily invoke your test runner 50 or even 100 times before all tests are passing. Any action we take that often is a natural candidate for automation.
Leaving aside automatic test running on file saving for the time being, the ideal automation for running tests is probably having your editor send the file name and line number to your test runner (e.g. RSpec). I know at one point, the common AO Vim configuration had a keyboard shortcut command to do just that.
But it’s Not Always that Simple
Defining a shortcut task that calls your test runner directly can break down in our modern polyglot world. Let’s say you are writing a React app with a Rails backend. You write unit tests for your React JavaScript with Mocha, unit tests for your Rails app with RSpec, and system tests with Cucumber.
Now we’ve got a bit of a problem. Namely, ‘mocha’, ‘rspec’, and ‘cucumber’ are separate commands, so they can’t readily be bound to the same “run my tests” keyboard shortcut. You can, of course, set up three separate shortcuts, but this “smells” a bit and requires you to stop and think which shortcut you want to invoke.
One “Run Task” Shortcut to Rule Them All
To eliminate this cognitive load, I’ve created the run_me
command. Really, it is useful for any task where you might use different command line programs for the same semantic meaning (e.g. “build” or “execute”).
run_me
takes three arguments:
config
– the path to a JSON file which will match filenames with commands to runfile
– the filename that you want to “run” in some way (test, build, execute)line
– an optional line number giving more context about what to run
The config file is really the interesting bit. run_me
expects a JSON file with a list of “runners,” which first determine if the filename sent in matches, then use the filename to run another command. Showing an example is probably the easiest way to explain how it works:
{
"runners": [
{
"regex": "spec\\.ts$", // file.match(/spec\.ts$/)
"cmd": "yarn test ${file}"
},
{
"regex": "features", // file.match(/features/)
"cmd": "bundle exec cucumber ${fileLine(':')}"
},
{
"regex": "spec", // file.match(/spec/)
"cmd": "bundle exec rspec ${fileLine(':')}"
}
]
}
How it Works
run_me
will use the input filename and the find the first runner whose regex
matches. Then it looks at the cmd
for that runner. The cmd
should be a string which can be used with JavaScript template literals (in other words, escape with ${}). The following variables are available:
file
– The filename that was passed inline
– The line number that was passed in (or null if none was passed in)match
– The RegExp match array (useful if you want to only send part of the filename in to your command)fileLine(joinStr)
– A function which can be invoked to returnfile
joined withline
number, using a join string of your choice. Useful to combine into commonly accepted formats like “filename.rb:32.” If line number was not provided,fileLine()
will only returnfile
, regardless of the value ofjoinStr
.
Processing Output
run_me
transfers execution to the cmd
you provide, so output will appear as it comes in. And, of course, the output is available for automated parsing and handling if your editor supports it.
Give it a Whirl
run_me
is available on npm. Give it a try, and see if you can make your repetitive tasks incur less cognitive load.