How to Unit Test a CLI in Go?

Introduction

In the previous articles, we

  • Created a CLI application using Go and Cobra
  • Restructured the code to be extensible, maintainable, and unit testable

In this article, we will write unit tests for our CLI application. Let's get started.

Unit Test- Root Command

In the cmd/greeter directory, create a new file called root_test.go. As the name implies, this file will hold the unit test for the root command. Add the following test to the file.

// cmd/greeter/root_test.go

package greeter_test

import (
	"bytes"
	"greeter/cmd/greeter"
	"testing"
)

func TestRootCmd_Execute(t *testing.T) {
	// Create the root command
	cmd := greeter.RootCommand()

	// Redirect stdout to a buffer to capture the output
	var stdout bytes.Buffer
	cmd.SetOut(&stdout)

	// Execute the root command
	err := cmd.Execute()

	// Check for any execution error
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}

	// Check the output
	expectedOutput := "Welcome to Greeter!"
	if stdout.String() != expectedOutput {
		t.Errorf("Expected output: %q, but got: %q", expectedOutput, stdout.String())
	}
}

I have added inline code comments so that the code is self-explanatory.

Unit Test - Greet Command

Next, we create another file called greet_test.go into the cmd/greeter directory. As the name implies, this file will hold the unit test for the greet command. Add the following test to the file.

// cmd/greeter/greet_test.go

package greeter_test

import (
	"bytes"
	"greeter/cmd/greeter"
	"testing"
)

func TestGreetCmd_Execute(t *testing.T) {
	// Create the greet command
	cmd := greeter.GreetCommand()

	// Redirect stdout to a buffer to capture the output
	var stdout bytes.Buffer
	cmd.SetOut(&stdout)

	// Set the arguments
	cmd.SetArgs([]string{"John"})

	// Execute the greet command with an argument
	err := cmd.Execute()
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}

	// Check the output
	expectedOutput := "Hello, John!\n"
	if stdout.String() != expectedOutput {
		t.Errorf("Expected output: %q, but got: %q", expectedOutput, stdout.String())
	}
}

As earlier, I have added inline code comments so that the code is self-explanatory.

To run the tests, head over to the terminal, execute the following command, and see a similar output.

~/workspace/greeter via 🐹 v1.20.2 at ☸️  kind-kind
➜ go test ./... -v

?   	greeter/cmd	[no test files]
=== RUN   TestGreetCmd_Execute
--- PASS: TestGreetCmd_Execute (0.00s)
=== RUN   TestRootCmd_Execute
--- PASS: TestRootCmd_Execute (0.00s)
PASS
ok  	greeter/cmd/greeter	0.160s

Conclusion

We have successfully added initial unit tests for both- root and greet commands of our CLI application. As your application grows, it becomes even more essential to have unit tests in place to test if the business logic is working as expected. Next, we will see how to add required and optional flags to a command. 


Similar Articles