How to implement unit tests for CLI commands in go - unit-testing

I'm starting a new OSS CLI tool that utilizes spf13/cobra. Being new to golang, I'm having a hard time figuring out the best way to test commands in isolation. Can anybody give me an example of how to test a command? Couple of caveats:
you can't return a cobra.Command from your init function
you can't have get_test.go in the cmd directory...which I was under the impression was the golang best practice.
I'm new to golang, go easy on me :sweat_smile:
Please correct me if I'm wrong.
Here's the cmd I'm trying to test: https://github.com/sahellebusch/raider/blob/3-add-get-alerts/cmd/get.go.
Open to ideas, suggestions, criticisms, whatever.

There are multiple approaches to implementing a CLI using go. This is the basic structure of the CLI I developed which is mostly influenced by the docker CLI and I have added unit tests as well.
The first thing you need is to have CLI as an interface. This will be inside a package named "cli".
package cli
type Cli interface {
// Have interface functions here
sayHello() error
}
This will be implemented by 2 clis: HelloCli (Our real CLI) and MockCli (used for unit tests)
package cli
type HelloCli struct {
}
func NewHelloCli() *HelloCli {
cli := &HelloCli{
}
return cli
}
Here the HelloCli will implement sayHello function as follows.
package cli
func (cli *HelloCli) SayHello() error {
// Implement here
}
Similarly, there will be a mock cli in a package named test that would implement cli interface and it will also implement the sayHello function.
package test
type MockCli struct {
}
func NewMockCli() *HelloCli {
cli := &MockCli{
}
return cli
}
func (cli *MockCli) SayHello() error {
// Mock implementation here
}
Now I will show how the command is added. First I would have the main package and this is where I would add all the new commands.
package main
func newCliCommand(cli cli.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "foo <command>"
}
cmd.AddCommand(
newHelloCommand(cli),
)
return cmd
}
func main() {
helloCli := cli.NewHelloCli()
cmd := newCliCommand(helloCli)
if err := cmd.Execute(); err != nil {
// Do something here if execution fails
}
}
func newHelloCommand(cli cli.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "hello",
Short: "Prints hello",
Run: func(cmd *cobra.Command, args []string) {
if err := pkg.RunHello(cli, args[0]); err != nil {
// Do something if command fails
}
},
Example: " foo hello",
}
return cmd
}
Here, I have one command called hello. Next, I will have the implementation in a separate package called "pkg".
package pkg
func RunHello(cli cli.Cli) error {
// Do something in this function
cli.SayHello()
return nil
}
The unit tests will also be contained in this package in a file named hello_test.
package pkg
func TestRunHello(t *testing.T) {
mockCli := test.NewMockCli()
tests := []struct {
name string
}{
{
name: "my test 1",
},
{
name: "my test 2"
},
}
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
err := SayHello(mockCli)
if err != nil {
t.Errorf("error in SayHello, %v", err)
}
})
}
}
When you execute foo hello, the HelloCli will be passed to the sayHello() function and when you run unit tests, MockCli will be passed.

You can check how cobra itself does it - https://github.com/spf13/cobra/blob/master/command_test.go
Basically you can refactor the actual Command logic(the run function) into a separate function and test that function. You probably want to name your functions properly instead of just calling it run.

Related

Golang unittest with *s3.S3 object

How should I unittest following piece of code. I was trying to use coutnerfiter to fake input "*s3.S3" object, but it's not working for me. I am new to coutnerfiter and Go, Can someone please help me on that.
func (l *listContentImp) ListS3Content(client *s3.S3) (bool, error) {
listObj := &s3.ListObjectsV2Input{
Bucket: aws.String(l.bucket),
}
var err error
l.lObj, err = client.ListObjectsV2(listObj)
if err != nil {
return false, err
}
return true, nil
}
You shouldn't pass a reference to the s3.S3 struct. When using the AWS SDK for Go v1 you typically pass the services corresponding interface. For S3 this is s3iface.
The signature of your function would look like this:
func (l *listContentImp) ListS3Content(client s3iface.S3API) (bool, error)
Now every struct that you pass that implements at least one of the methods of s3iface.S3API will work.
At runtime you'll pass the proper service client, but in the unit tests you can just pass a mock:
type mock struct {
s3iface.S3API
output *s3.ListObjectsV2Output
err error
}
func (m mock) ListObjectsV2(*s3.ListObjectsV2Input) (*s3.ListObjectsV2Output, error) {
return m.output, m.err
}
In your test you create the mock and pass it to your function:
func Test_ListObject(t *testing.T) {
l := &listContentImp{...}
m := mock{
output: &s3.ListObjectsV2Output{...},
err: nil
}
result, err := l.ListS3Content(m)
[... add checks here...]
}

how to test method that prints to console in golang?

I have next struct.
package logger
import "fmt"
type IPrinter interface {
Print(value string)
}
type ConsolePrinter struct{}
func (cp *ConsolePrinter) Print(value string) {
fmt.Printf("this is value: %s", value)
}
Test coverage says I need to test that ConsolePrinter Print method.
How can I cover this method?
Thanks.
Following comment that #icza wrote, I've written test below.
func TestPrint(t *testing.T) {
rescueStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
cp := &ConsolePrinter{}
cp.Print("test")
w.Close()
out, _ := ioutil.ReadAll(r)
os.Stdout = rescueStdout
if string(out) != "this is value: test" {
t.Errorf("Expected %s, got %s", "this is value: test", out)
}
}
I've found example in question What is the best way to convert byte array to string?.
Use Examples to convey the usage of a function.
Don't fret over 100% test coverage, especially for simple straightforward functions.
func ExampleHello() {
fmt.Println("hello")
// Output: hello
}
The additional benefit is that examples are outputted in a generated doc with go doc tool.
I would recommend to create new instance of the logger, which would behave exactly as fmt methods for printing data to console. Also, you can configure it with additional features like showing the filename, date and etc. Such custom logger can be passed as a parameter to your service/instance factory method. That would make it really easy to mock and test.
Your code
type Logs interface {
Println(v ...interface{})
}
type InstanceToTest struct {
log Logs
}
func InstanceToTestFactory(logger Logs) *InstanceToTest {
return &InstanceToTest{logger}
}
func (i *InstanceToTest) SomeMethod(a string) {
i.log.Println(a)
}
Create a mock for logger
type LoggerMock struct {
CalledPrintln []interface{}
}
func (l *LoggerMock) Println(v ...interface{}) {
l.CalledPrintln = append(CalledPrintln, v)
}
And in your test
func TestInstanceToTestSomeMethod(t *testing.T) {
l := &LoggerMock{}
i := InstanceToTestFactory(l)
a := "Test"
i.SomeMethod(a)
if len(l.CalledPrintln) == 0 || l.CalledPrintln[0] != a {
t.Error("Not called")
}
}

setup and teardown for each test using std testing package

I am using go "testing" package. Running my tests like below.
func TestMain(m *testing.M) {
...
// Setup
os.Exit(m.Run())
// Teardown
}
This will run a setup before any test is run, and a teardown after all tests are complete. And I do need this, as the setup sets the DB up. But also, I need, and yet to find out a way to run a per-test setup/teardown. For the unit tests I am running, I would like to clear the DB before every test, so that there are no issues with the content of the DB causing unexpected behavior.
Update for Go 1.14 (Q1 2020)
The testing package now supports cleanup functions, called after a test or benchmark has finished, by calling T.Cleanup or B.Cleanup respectively. Example,
func TestFunction(t *testing.T) {
// setup code
// sub-tests
t.Run()
t.Run()
...
// cleanup
t.Cleanup(func(){
//tear-down code
})
}
Here, t.Cleanup runs after the test and all its sub-tests are complete.
Original answer (Feb. 2017)
As shown in the article "Go unit test setup and teardown" from Kare Nuorteva, you could use a setup function which returns... a teardown function to you defer.
See this gist:
func setupSubTest(t *testing.T) func(t *testing.T) {
t.Log("setup sub test")
return func(t *testing.T) {
t.Log("teardown sub test")
}
}
The setup function is in charge of defining and returning the teardown one.
For each test, for instance in a table-driven test scenario:
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
teardownSubTest := setupSubTest(t)
defer teardownSubTest(t)
result := Sum(tc.a, tc.b)
if result != tc.expected {
t.Fatalf("expected sum %v, but got %v", tc.expected, result)
}
})
}
If table driven test pattern works for you, you should stick with it. If you need something more generic and flexible feel free to give https://github.com/houqp/gtest a try.
Here is a quick example:
import (
"strings"
"testing"
"github.com/houqp/gtest"
)
type SampleTests struct{}
// Setup and Teardown are invoked per test group run
func (s *SampleTests) Setup(t *testing.T) {}
func (s *SampleTests) Teardown(t *testing.T) {}
// BeforeEach and AfterEach are invoked per test run
func (s *SampleTests) BeforeEach(t *testing.T) {}
func (s *SampleTests) AfterEach(t *testing.T) {}
func (s *SampleTests) SubTestCompare(t *testing.T) {
if 1 != 1 {
t.FailNow()
}
}
func (s *SampleTests) SubTestCheckPrefix(t *testing.T) {
if !strings.HasPrefix("abc", "ab") {
t.FailNow()
}
}
func TestSampleTests(t *testing.T) {
gtest.RunSubTests(t, &SampleTests{})
}
You may consider having a table of functions subTestSetup, subTest and subTestTeardown passing the db connection/other common items in a struct (subTestSetup can return this struct). You can possibly reuse some/parts of the setup & tear down in different functions too & keep this modular as your testing requirement grows. Call defer subTestTeardown() before you call the subTest, to ensure tear down code executes even if there's any issue with subTest.
⚠️ NOTE: This is not a fully tested solution; it just seems to work very well in casual test scenarios.
In a simple case, I've used my own implementation of t.Run() that does the cleaning after a sub-test has been finished:
func Run(t *testing.T, name string, f func(*testing.T)) bool {
result := t.Run(name, f)
// Do the cleanup.
return result
}
And in my tests:
func TestSomethingWorks(t *testing.T) {
Run(t, "scenario 1", func(t *testing.T) {
// ...
})
Run(t, "scenario 2", func(t *testing.T) {
// ...
})
}

How to unit test google cloud storage in golang?

I'm writing an appengine app in Go that uses Google cloud storage.
For example, my "reading" code looks like:
client, err := storage.NewClient(ctx)
if err != nil {
return nil, err
}
defer func() {
if err := client.Close(); err != nil {
panic(err)
}
}()
r, err := client.Bucket(BucketName).Object(id).NewReader(ctx)
if err != nil {
return nil, err
}
defer r.Close()
return ioutil.ReadAll(r)
... where ctx is a context from appengine.
When I run this code in a unit test (using aetest), it actually sends requests to my cloud storage; I'd like to run this hermetically instead, similar to how aetest allows fake datastore calls.
(Possibly related question, but it deals with python, and the linked github issue indicates it's solved in a python-specific way).
How can I do this?
One approach, also suggested here is to allow your GCS client to have its downloader swapped out for a stub while unit testing. First, define an interface that matches how you use the Google Cloud Storage library, and then reimplement it with fake data in your unit tests.
Something like this:
type StorageClient interface {
Bucket(string) Bucket // ... and so on, matching the Google library
}
type Storage struct {
client StorageClient
}
// New creates a new Storage client
// This is the function you use in your app
func New() Storage {
return NewWithClient(&realGoogleClient{}) // provide real implementation here as argument
}
// NewWithClient creates a new Storage client with a custom implementation
// This is the function you use in your unit tests
func NewWithClient(client StorageClient) {
return Storage{
client: client,
}
}
It can be a lot of boilerplate to mock entire 3rd party APIs, so maybe you'll be able to make it easier by generating some of those mocks with golang/mock or mockery.
I have done something like this...
Since storage client is sending HTTPS request so I mocked the HTTPS server using httptest
func Test_StorageClient(t *testing.T) {
tests := []struct {
name string
mockHandler func() http.Handler
wantErr bool
}{
{
name: "test1",
mockHandler: func() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("22\n96\n120\n"))
return
})
},
wantErr: false,
},
{
name: "test2 ",
mockHandler: func() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
return
})
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
serv := httptest.NewTLSServer(tt.mockHandler())
httpclient := http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
client, _ := storage.NewClient(context.Background(), option.WithEndpoint(serv.URL), option.WithoutAuthentication(), option.WithHTTPClient(&httpclient))
got, err := readFileFromGCS(client)
if (err != nil) != tt.wantErr {
t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
return
}
})
}
}
Cloud Storage on the Python Development server is emulated using local files with the Blobstore service, which is why the solution of using a Blobstore stub with testbed (also Python-specific) worked. However there is no such local emulation for Cloud Storage on the Go runtime.
As Sachin suggested, the way to unit test Cloud Storage is to use a mock. This is the way it's done internally and on other runtimes, such as node.
I would advice you reduce the mocks as much as possible you might need to use an hermetic approach to make it almost similar to the real thing .
https://testing.googleblog.com/2012/10/hermetic-servers.html

How can I do test setup using the testing package in Go

How can I do overall test setup processing which sets the stage for all the tests when using the testing package?
As an example in Nunit there is a [SetUp] attribute.
[TestFixture]
public class SuccessTests
{
[SetUp] public void Init()
{ /* Load test data */ }
}
Starting with Go 1.4 you can implement setup/teardown (no need to copy your functions before/after each test). The documentation is outlined here in the Main section:
TestMain runs in the main goroutine and can do whatever setup and
teardown is necessary around a call to m.Run. It should then call
os.Exit with the result of m.Run
It took me some time to figure out that this means that if a test contains a function func TestMain(m *testing.M) then this function will be called instead of running the test. And in this function I can define how the tests will run. For example I can implement global setup and teardown:
func TestMain(m *testing.M) {
setup()
code := m.Run()
shutdown()
os.Exit(code)
}
A couple of other examples can be found here.
The TestMain feature added to Go’s testing framework in the latest
release is a simple solution for several testing use cases. TestMain
provides a global hook to perform setup and shutdown, control the
testing environment, run different code in a child process, or check
for resources leaked by test code. Most packages will not need a
TestMain, but it is a welcome addition for those times when it is
needed.
This can be achieved by putting a init() function in the myfile_test.go file. This will be run before the init() function.
// myfile_test.go
package main
func init() {
/* load test data */
}
The myfile_test.init() will be called before the package init() function.
Given a simple function to unit test:
package math
func Sum(a, b int) int {
return a + b
}
You can test it with a setup function that returns teardown function. And after calling setup() you can make a deferred call to teardown().
package math
import "testing"
func setupTestCase(t *testing.T) func(t *testing.T) {
t.Log("setup test case")
return func(t *testing.T) {
t.Log("teardown test case")
}
}
func setupSubTest(t *testing.T) func(t *testing.T) {
t.Log("setup sub test")
return func(t *testing.T) {
t.Log("teardown sub test")
}
}
func TestAddition(t *testing.T) {
cases := []struct {
name string
a int
b int
expected int
}{
{"add", 2, 2, 4},
{"minus", 0, -2, -2},
{"zero", 0, 0, 0},
}
teardownTestCase := setupTestCase(t)
defer teardownTestCase(t)
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
teardownSubTest := setupSubTest(t)
defer teardownSubTest(t)
result := Sum(tc.a, tc.b)
if result != tc.expected {
t.Fatalf("expected sum %v, but got %v", tc.expected, result)
}
})
}
}
Go testing tool will report the logging statements in the shell console:
% go test -v
=== RUN TestAddition
=== RUN TestAddition/add
=== RUN TestAddition/minus
=== RUN TestAddition/zero
--- PASS: TestAddition (0.00s)
math_test.go:6: setup test case
--- PASS: TestAddition/add (0.00s)
math_test.go:13: setup sub test
math_test.go:15: teardown sub test
--- PASS: TestAddition/minus (0.00s)
math_test.go:13: setup sub test
math_test.go:15: teardown sub test
--- PASS: TestAddition/zero (0.00s)
math_test.go:13: setup sub test
math_test.go:15: teardown sub test
math_test.go:8: teardown test case
PASS
ok github.com/kare/go-unit-test-setup-teardown 0.010s
%
You can pass some additional parameters to setup/teardown with this approach.
The Go testing framework doesn't have anything equivalent to NUnit's SetUp attribute (marking a function to be called before each test in the suite). There are a few options though:
Simply call your SetUp function from each test where it is needed.
Use an extension to Go's testing framework that implements xUnit paradigms and concepts. Three strong options come to mind:
gocheck
testify
gunit
Each of these libraries encourage you to organize your tests into suites/fixtures similar to other xUnit frameworks, and will call the setup methods on the suite/fixture type before each of the Test* methods.
With the following template, you can make a one line call in each TestMethod that does both setup and tear-down.
func setupTest() func() {
// Setup code here
// tear down later
return func() {
// tear-down code here
}
}
func TestMethod(t *testing.T) {
defer setupTest()()
// asserts, ensures, requires... here
}
Typically, tests in go aren't written in the same style as other languages. Often, there's relatively fewer test functions, but each contains a table-driven set of test cases. See this article written by one of the Go team.
With a table-driven test, you simply put any setup code before the loop that executes the individual test-cases specified in the table, and put any cleanup code afterwards.
If you still have shared setup code between test functions, you can extract the shared setup code into a function, and use a sync.Once if it's important that it's executed exactly once (or as another answer suggests, use init(), but this has the disadvantage that the setup will be done even if the test cases aren't run (perhaps because you've limited the test cases by using go test -run <regexp>.)
I'd say if you think you need shared setup between different tests that gets executed exactly once you should have a think if you really need it, and if a table-driven test wouldn't be better.
In case someone is looking an alternative of #BeforeEach (which runs before each test in a test file) and #AfterEach (which runs after test in a test file), here's a helper snippet.
func CreateForEach(setUp func(), tearDown func()) func(func()) {
return func(testFunc func()) {
setUp()
testFunc()
tearDown()
}
}
You can use it like below with help of TestMain
var RunTest = CreateForEach(setUp, tearDown)
func setUp() {
// SETUP METHOD WHICH IS REQUIRED TO RUN FOR EACH TEST METHOD
// your code here
}
func tearDown() {
// TEAR DOWN METHOD WHICH IS REQUIRED TO RUN FOR EACH TEST METHOD
// your code here
}
fun TestSample(t *testing.T) {
RunTest(func() {
// YOUR CODE HERE
})
}
also you can check: go-beforeeach
You can use the testing package for test setup - which will set the stage for all tests and teardown - which will cleanup the stage after tests have run.
The below calculates the area of a rectangle:
package main
import (
"errors"
"fmt"
)
func area(height float64, width float64) (float64, error) {
if height == width {
fmt.Printf("Rectangle with given dimension is a square. Area is: %f\n", height * width)
return height * width, nil
} else if height <= 0 || width <= 0 {
return 0, errors.New("Both dimensions need to be positive")
} else {
fmt.Printf("Area is: %f\n", height * width)
return height * width, nil
}
}
func main() {
var length float64 = 4.0
var breadth float64 = 5.0
area(length, breadth)
}
This is the implementation for test setup and teardown using TestMain as Salvador Dali's explains. (Note that since v1.15 the TestMain function is no longer required to call os.Exit [ref])
package main
import (
"log"
"testing"
)
var length float64
var breadth float64
func TestMain(m *testing.M) {
setup()
m.Run()
teardown()
}
func setup() {
length = 2.0
breadth = 3.0
log.Println("\n-----Setup complete-----")
}
func teardown() {
length = 0
breadth = 0
log.Println("\n----Teardown complete----")
}
func TestAreaOfRectangle(t *testing.T) {
val, err := area(length, breadth)
want := 6.0
if val != want && err != nil {
t.Errorf("Got %f and %v; Want %f and %v", val, err, want, nil)
}
}
And this is the implementation for test setup and teardown using sub-tests:
package main
import "testing"
func TestInvalidRectangle(t *testing.T) {
// setup
var length float64 = -2.0
var breadth float64 = 3.0
t.Log("\n-----Setup complete for invalid rectangle-----")
// sub-tests
t.Run("invalid dimensions return value", func(t *testing.T) {
val, _ := area(length, breadth)
area := 0.0
if val != area {
t.Errorf("Got %f; Want %f", val, area)
}
})
t.Run("invalid dimensions message", func(t *testing.T) {
_, err := area(length, breadth)
want := "Both dimensions need to be positive"
if err.Error() != want {
t.Errorf("Got error: %v; Want error: %v", err.Error(), want)
}
})
// teardown
t.Cleanup(func(){
length = 0
breadth = 0
t.Log("\n----Teardown complete for invalid rectangle----")
})
}
func TestRectangleIsSquare(t *testing.T) {
var length float64 = 3.0
var breadth float64 = 3.0
t.Log("\n-----Rectangle is square setup complete-----")
t.Run("valid dimensions value and message", func(t *testing.T) {
val, msg := area(length, breadth)
area := 9.0
if val != area && msg != nil {
t.Errorf("Got %f and %v; Want %f and %v", val, msg, area, nil)
}
})
t.Cleanup(func(){
length = 0
breadth = 0
t.Log("\n----Rectangle is square teardown Complete----")
})
}
Shameless plug, I created https://github.com/houqp/gtest to help solve exactly this problem.
Here is a quick example:
import (
"strings"
"testing"
"github.com/houqp/gtest"
)
type SampleTests struct{}
// Setup and Teardown are invoked per test group run
func (s *SampleTests) Setup(t *testing.T) {}
func (s *SampleTests) Teardown(t *testing.T) {}
// BeforeEach and AfterEach are invoked per test run
func (s *SampleTests) BeforeEach(t *testing.T) {}
func (s *SampleTests) AfterEach(t *testing.T) {}
func (s *SampleTests) SubTestCompare(t *testing.T) {
if 1 != 1 {
t.FailNow()
}
}
func (s *SampleTests) SubTestCheckPrefix(t *testing.T) {
if !strings.HasPrefix("abc", "ab") {
t.FailNow()
}
}
func TestSampleTests(t *testing.T) {
gtest.RunSubTests(t, &SampleTests{})
}
You can create as any test group you want within a package with each of them using a different set of setup/teardown routines.
Here is the minimal test suit framework to run subtests with
BeforeAll, ran before all subtests in a test
BeforeEach, ran before each subtest
AfterEach, ran after each subtest
AfterAll, ran after all subtests in a test
package suit
import "testing"
func Of(subTests *SubTests) *SubTests {
if subTests.AfterAll != nil {
subTests.T.Cleanup(subTests.AfterAll)
}
return subTests
}
type SubTests struct {
T *testing.T
BeforeEach func()
AfterEach func()
AfterAll func()
}
func (s *SubTests) TestIt(name string, f func(t *testing.T)) {
if s.AfterEach != nil {
defer s.AfterEach()
}
if s.BeforeEach != nil {
s.BeforeEach()
}
s.T.Run(name, f)
}
Usage
func TestFoo(t *testing.T) {
// BeforeAll setup goes here
s := suit.Of(&suit.SubTests{
T: t,
BeforeEach: func() { ... },
AfterEach: func() { ... },
AfterAll: func() { ... },
})
s.TestIt("returns true", func(t *testing.T) {
assert.Equal(t, 1, 1)
})
}