Unit Testing CLI Programs in Go

Elliot Chance
Feb 29 · 2 min read
Image for post
Image for post
Photo by John Schnobrich on Unsplash

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

  1. We now have an out which makes it much easier to capture output.
  2. We no longer use the functions in the flag package. Instead we have to instantiate a new flag parser for each call to main() .
  3. Anywhere output is generated will have to write to out instead.

Now our unit test needs only know about the CLI arguments before calling main():

This looks a lot nicer when there are multiple tests:

The Startup

Medium's largest active publication, followed by +732K people. Follow to join our community.

Elliot Chance

Written by

I’m a data nerd and TDD enthusiast originally from Sydney. Currently working for Uber in New York. My thoughts here are my own. 🤓 elliotchance@gmail.com

The Startup

Medium's largest active publication, followed by +732K people. Follow to join our community.

Elliot Chance

Written by

I’m a data nerd and TDD enthusiast originally from Sydney. Currently working for Uber in New York. My thoughts here are my own. 🤓 elliotchance@gmail.com

The Startup

Medium's largest active publication, followed by +732K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store