Contents

How to Unit Test a CLI in Go?

In this post, we will learn about writing unit tests for a CLI created with Cobra in Go.


Introduction

In the previous articles, we:

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


In case you prefer following along a video, you can checkout the following video on my YouTube channel.


Note
The project structure in the video is a little different from the one referred in this article. In video, we put the root, and greet command in their own packages. However, both approaches are correct. You may pick one as per your use case.

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 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())
	}
}

In case you prefer following along a video, you can checkout the following video on my YouTube channel.


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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 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.


Executing Tests

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
~/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 - the root and the greet command 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.