It’s a common scenario — for me at least — to be building a CLI tool (
main package) that has CLI options (
flags package) that I want to add unit tests to.
Here’s a simple example of a CLI tool that adds or multiplies some input numbers:
Let’s try it:
5 + 7 + 9 = 21 — great… But wait!
3 * 2 * 5 != 0…I’ve put a deliberate bug in the command. Something that we will catch and fix with a unit test.
Don’t Try This At Home Kids
Let’s write a unit test that uses the same CLI arguments so we can fix the bug. However, we have a lot of global state that needs to be manipulated:
Yuck! This is really ugly and problematic code… miles away from a small, easy to read unit test.
One fairly obvious solution here is to separate the logic that does the calculating from the
main() function (that handles the arguments and output), like:
Now the unit test is trivial:
But this is cheating! Since we have bypassed the flags completely, we are not actually testing the CLI arguments. We’d really like to perform black-box testing on the tool as it would be used — instead of testing isolated parts. Especially if a bug was introduced to the way flags are parsed.
A Better Solution
- We now have an
outwhich makes it much easier to capture output.
- We no longer use the functions in the
flagpackage. Instead we have to instantiate a new flag parser for each call to
- Anywhere output is generated will have to write to
Now our unit test needs only know about the CLI arguments before calling
This looks a lot nicer when there are multiple tests: