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) {
// ...
})
}
Related
I have a function that accepts a path and an S3Client:
ListObjects(folder, s3Client)
I want it to accept a "real" client in production. I.e: s3.New(session.New()
But when I run my tests I'd prefer it to accept my mocked version so that we don't talk to AWS in our tests.
My approach to achieve this is to make an interface that matches both the real S3Client and my mocked version.
Is this the best approach or am I missing anything?
First I have defined the interface with the function I plan to mock.
package core
import "github.com/aws/aws-sdk-go/service/s3"
type MyS3ClientInterface interface {
ListObjects(input *s3.ListObjectsInput) (*s3.ListObjectsOutput, error)
}
GetObjectsFromS3 is implemented like this:
// GetObjectsFromS3 returns a list of objects found in provided path on S3
func GetObjectsFromS3(path string, s3Client core.MyS3ClientInterface) ([]*core.Asset, error) {
// build input variable
result, err := s3Client.ListObjects(input)
// cut..
}
Here's my mocked version of s3Client.ListObjects
package test_awstools
import (
"github.com/aws/aws-sdk-go/service/s3"
)
type MyS3Client struct{}
func (m MyS3Client) ListObjects(input *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) {
output := &s3.ListObjectsOutput{}
return output, nil
}
func (m MyS3Client) GetObject(input *s3.GetObjectInput) (*s3.GetObjectOutput, error) {
// output := &s3.GetObject()
return nil, nil
}
internal/awstools/bucket_test.go
1 package awstools
2
3 import (
4 "fmt"
5 "testing"
6
7 "internal/test_awstools"
8 )
9
10 func TestListObjects(t *testing.T) {
11 folder := "docs"
// Use my mocked version of the S3 client.
12 s3Client := &test_awstools.MyS3Client{}
// And I pass that along to the ListObject function
13 objects, err := ListObjects(folder, s3Client)
14 if err != nil {
15 t.Error(err)
16 }
17 fmt.Println(objects)
18 }
One small improvement I could suggest would be to allow your mock struct to accept a function, that way it can vary between tests. Something like this:
package test_awstools
import (
"github.com/aws/aws-sdk-go/service/s3"
)
type MyS3Client struct {
ListObjectsFunc func(*s3.ListObjectsInput) (*s3.ListObjectsOutput, error)
GetObjectFunc func(*s3.GetObjectInput) (*s3.GetObjectOutput, error)
}
func (m *MyS3Client) ListObjects(input *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) {
return m.ListObjectsFunc(input)
}
func (m *MyS3Client) GetObject(input *s3.GetObjectInput) (*s3.GetObjectOutput, error) {
return m.GetObjectFunc(input)
}
Now in your test you can set the *Func values on your struct so that your test is much more declarative about the stubbed behavior:
func TestListObjects(t *testing.T) {
folder := "docs"
// Use my mocked version of the S3 client.
s3Client := &test_awstools.MyS3Client{}
// Set the ListObjectsFunc to behave the way you want
s3Client.ListObjectsFunc = func(input *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) {
output := &s3.ListObjectsOutput{}
return output, nil
}
// Make the call to list objects (which is now mocked)
objects, err := s3Client.ListObjects(folder, s3Client)
// Do your assertions
if err != nil {
t.Error(err)
}
fmt.Println(objects)
}
I have written a function:
func AllItems(w http.ResponseWriter, r *http.Request) {
db, err := gorm.Open("sqlite3", "test.db")
if err != nil {
panic("failed to connect database")
}
defer db.Close()
var items [] Item
db.Find(&items)
fmt.Println("{}", items)
json.NewEncoder(w).Encode(items)
}
I want to do unit testing on this. Ideally, unit testing means that each line of the function needs to be tested. I'm not sure how I should test if a database connection is opened and then if it is displaying all the contents of the database. How should I test this code?
This function is the GET endpoint of a simple CRUD application. The code is here.
Refactor your code and break it down into smaller, testable functions which you pass dependencies to. Also create interfaces for the dependencies to make testing easier.
For example:
type myDatabaseInterface interface {
Find(interface{}) // this signature should match the real db.Find()
}
func AllItems(w http.ResponseWriter, r *http.Request) {
db, err := gorm.Open("sqlite3", "test.db")
if err != nil {
panic("failed to connect database")
}
defer db.Close()
items := findItems(db)
json.NewEncoder(w).Encode(items)
}
func find(db myDatabaseInterface) ([]Item) {
var items []Item
db.Find(&items)
return items
}
Then you can create mocks for your dependencies and use them in your tests:
type mock struct {}
// mock should implement myDatabaseInterface to be able to pass it to the function
func (m *mock) Find(interface{}) {
// implement the mock to satisfy your test
}
func Test_find(t *testing.T) {
m := mock{}
res := find(m)
// do testing
}
Instead of calling Open every time you handle a request maybe you should open it outside and have it available to your function. That way the handler becomes so small there's no need to test it really:
func makeAllItemsHandler(db myDatabaseInterface) func(http.ResponseWriter, *http.Request) {
return func(http.ResponseWriter, *http.Request) {
items := findItems(db)
json.NewEncoder(w).Encode(items)
}
}
Then you can create a db once and for all when you set up your application and pass it to the functions that need it thus removing hard to test code from the functions.
In other test frameworks that I have used, when writing test helpers, it's nice to be able to automatically test them, i.e. test that they fail as well as pass.
Let me use the following for the helper (in reality it's a lot more complex):
func IsRedirect(t *testing.T, code int) {
assert.True(t, code >= 300)
assert.True(t, code < 400)
}
Then sure, I can write:
func TestIsRedirect(t *testing.T) {
IsRedirect(t, http.StatusSeeOther)
}
But I'd also like to write something like:
func TestNotRedirect(t *testing.T) {
t.RequireFailure()
IsRedirect(t, http.StatusOK)
}
Note, I don't really want to write:
func IsRedirect(code int) bool
because the function has more than just one or two conditions that can fail, and I want to be able to assert on the specific failure case.
You could do something like the following:
func IsRedirect(t *testing.T, code int, b bool) {
assert.Equal(t, b, code >= 300)
assert.Equal(t, b, code < 400)
}
func TestNotRedirect(t *testing.T) {
IsRedirect(t, http.StatusOK, false)
}
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 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)
})
}