How do I test Go's "testing" package functions? - unit-testing

I'm writing a library of testing utility functions and I would like them to be themselves tested.
An example of one such function is:
func IsShwifty(t *testing.T, foo string) bool {
result, err := bar.MyFunction(string)
if err != nil {
t.Error("Failed to get shwifty: " + foo, err)
}
return result == "shwifty"
}
I would like to write a TestIsShwifty that feeds in something to make MyFunction return an error and then make t.Error. Then, I want to have the TestIsShwifty pass with something like:
func TestIsShwifty(t *testing.T) {
if doesError(t, IsShwifty(t)) == false {
t.Error("Oh know! We didn't error!")
}
}
Is this possible in Go?

I figured it out!
I just needed to create a separate instance of testing.T.
func TestIsShwifty(t *testing.T) {
newT := testing.T{}
IsShwifty(newT)
if newT.Failed() == false {
t.Error("Test should have failed")
}
}

Related

Golang how to test function that return channel type?

I try to test function StartP,
Expect that Start() should be called 1 times, Done() should be called 1 times
but I have trouble that test will block when run this step <-ps.Done()
I expect <-ps.Done() return nil
How can I test function that return chan type?
// production code
func (s *vService) StartP(ctx context.Context, reason string) error {
ps, err := s.factory.CreateVService(ctx)
if err != nil {
return err
}
ps.Start(reason)
err = <-ps.Done() // code stop here to wait ? how can i test ?
if err != nil {
return err
}
return nil
}
// test code
func Test_StartP(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
mockPService := mockpservice.NewMockPInterface(mockCtrl)
vService := &vService {
factory: &servicefactory.FakeServiceFactory{
MockPService: mockPService
}
}
mockPService.EXPECT().Start("reason").Times(1).Return()
mockPService.EXPECT().Done().Times(1).DoAndReturn(func() chan error {
return nil
})
err := vService.StartP(context.Background(), "reason")
assert.Equal(t, nil, err)
}
I use gomock to mock the PServiceInterface
// interface
type PServiceInterface interface {
Start(reason string)
Done() <-chan error
}
gomock gen this function
func (m *MockProvisionServiceInterface) Done() <-chan error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Done")
ret0, _ := ret[0].(<-chan error)
fmt.Println(ret0,".....mock Done()")
return ret0
}
// I also try this
mockProvisionService.EXPECT().Done().Times(1).DoAndReturn( func() chan error {
fmt.Println("DoAndReturn...err nil")
ch := make(chan error, 1)
ch <- nil
return ch
})
The following shows, I think, the minimum code to implement your test goals.
It does not use any mocking framework because in my experience they tend to obfuscate the test intent, require everybody in the team to learn how to use them and are not needed, at least in Go. One could also wonder what the test is actually testing...
First, let's add some missing production code:
type factoryInterface interface {
CreateVService(ctx context.Context) (PServiceInterface, error)
}
type vService struct {
factory factoryInterface
}
And now the test code, in three parts: the factory, the mock, and the test.
The test factory:
type testFactory struct {
mock PServiceInterface
}
func (f *testFactory) CreateVService(ctx context.Context) (PServiceInterface, error) {
return f.mock, nil
}
The mock:
type ServiceMock struct {
records []string
}
func (sm *ServiceMock) Start(reason string) {
sm.records = append(sm.records, "start")
}
func (sm *ServiceMock) Done() <-chan error {
sm.records = append(sm.records, "done")
ch := make(chan error)
close(ch)
return ch
}
And finally the test:
func TestWithMock(t *testing.T) {
mock := ServiceMock{}
sut := &vService{factory: &testFactory{&mock}}
err := sut.StartP(context.Background(), "banana")
if err != nil {
t.Fatalf("StartP: have: %s; want: no error", err)
}
if have, want := len(mock.records), 2; have != want {
t.Fatalf("number of mock calls: have: %v; want: %v", have, want)
}
if have, want := mock.records[0], "start"; have != want {
t.Fatalf("mock call 1: have: %v; want: %v", have, want)
}
if have, want := mock.records[1], "done"; have != want {
t.Fatalf("mock call 2: have: %v; want: %v", have, want)
}
}
The three assertions on the mock call sequence can be collapsed into one, comparing directly the slice []string{"start", "done"}, if one is using a test library such as the excellent assert package of https://github.com/gotestyourself/gotest.tools
I found the answer, root cause is DoAndReturn something wrong.
Func type should be <-chan error, not chan error
mockProvisionService.EXPECT().Done().Times(1).DoAndReturn( func() <-chan error{
fmt.Println("DoAndReturn...err nil")
ch := make(chan error, 1)
ch <- nil
return ch
})

How do I mock a function that write result to it's argument in Go

I am writing unit test in golang by https://github.com/stretchr/testify
Suppose I have a method below,
func DoSomething(result interface{}) error {
// write some data to result
return nil
}
so the caller can call DoSomething as following
result := &SomeStruct{}
err := DoSomething(result)
if err != nil {
fmt.Println(err)
} else {
fmt.Println("The result is", result)
}
Now I know how to use testify or some other mocking tools to mock the returns value (it's err here) by something like
mockObj.On("DoSomething", mock.Anything).Return(errors.New("mock error"))
My question is "how do i mock the result argument" in this kind of scenario?
Since result is not a return value but a argument, the caller calls it by passing a pointer of a struct, and the function modify it.
You can use the (*Call).Run method:
Run sets a handler to be called before returning. It can be used when
mocking a method (such as an unmarshaler) that takes a pointer to a
struct and sets properties in such struct
Example:
mockObj.On("Unmarshal", mock.AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) {
arg := args.Get(0).(*map[string]interface{})
arg["foo"] = "bar"
})
As #bikbah said, here is an example:
services/message.go:
type messageService struct {
HttpClient http.Client
BaseURL string
}
func (m *messageService) MarkAllMessages(accesstoken string) []*model.MarkedMessage {
endpoint := m.BaseURL + "/message/mark_all"
var res model.MarkAllMessagesResponse
if err := m.HttpClient.Post(endpoint, &MarkAllMessagesRequestPayload{Accesstoken: accesstoken}, &res); err != nil {
fmt.Println(err)
return res.MarkedMsgs
}
return res.MarkedMsgs
}
We passes res to the m.HttpClient.Post method. In this method, the res will be populated with json.unmarshal method.
mocks/http.go:
package mocks
import (
"io"
"github.com/stretchr/testify/mock"
)
type MockedHttp struct {
mock.Mock
}
func (m *MockedHttp) Get(url string, data interface{}) error {
args := m.Called(url, data)
return args.Error(0)
}
func (m *MockedHttp) Post(url string, body interface{}, data interface{}) error {
args := m.Called(url, body, data)
return args.Error(0)
}
services/message_test.go:
package services_test
import (
"errors"
"reflect"
"strconv"
"testing"
"github.com/stretchr/testify/mock"
"github.com/mrdulin/gqlgen-cnode/graph/model"
"github.com/mrdulin/gqlgen-cnode/services"
"github.com/mrdulin/gqlgen-cnode/mocks"
)
const (
baseURL string = "http://localhost/api/v1"
accesstoken string = "123"
)
func TestMessageService_MarkAllMessages(t *testing.T) {
t.Run("should mark all messaages", func(t *testing.T) {
testHttp := new(mocks.MockedHttp)
var res model.MarkAllMessagesResponse
var markedMsgs []*model.MarkedMessage
for i := 1; i <= 3; i++ {
markedMsgs = append(markedMsgs, &model.MarkedMessage{ID: strconv.Itoa(i)})
}
postBody := services.MarkAllMessagesRequestPayload{Accesstoken: accesstoken}
testHttp.On("Post", baseURL+"/message/mark_all", &postBody, &res).Return(nil).Run(func(args mock.Arguments) {
arg := args.Get(2).(*model.MarkAllMessagesResponse)
arg.MarkedMsgs = markedMsgs
})
service := services.NewMessageService(testHttp, baseURL)
got := service.MarkAllMessages(accesstoken)
want := markedMsgs
testHttp.AssertExpectations(t)
if !reflect.DeepEqual(got, want) {
t.Errorf("got wrong return value. got: %#v, want: %#v", got, want)
}
})
t.Run("should print error and return empty slice", func(t *testing.T) {
var res model.MarkAllMessagesResponse
testHttp := new(mocks.MockedHttp)
postBody := services.MarkAllMessagesRequestPayload{Accesstoken: accesstoken}
testHttp.On("Post", baseURL+"/message/mark_all", &postBody, &res).Return(errors.New("network"))
service := services.NewMessageService(testHttp, baseURL)
got := service.MarkAllMessages(accesstoken)
var want []*model.MarkedMessage
testHttp.AssertExpectations(t)
if !reflect.DeepEqual(got, want) {
t.Errorf("got wrong return value. got: %#v, want: %#v", got, want)
}
})
}
In the unit test case, we populated the res in #Call.Run method and assigned the return value(res.MarkedMsgs) of service.MarkAllMessages(accesstoken) to got variable.
unit test result and coverage:
=== RUN TestMessageService_MarkAllMessages
--- PASS: TestMessageService_MarkAllMessages (0.00s)
=== RUN TestMessageService_MarkAllMessages/should_mark_all_messaages
TestMessageService_MarkAllMessages/should_mark_all_messaages: message_test.go:39: PASS: Post(string,*services.MarkAllMessagesRequestPayload,*model.MarkAllMessagesResponse)
--- PASS: TestMessageService_MarkAllMessages/should_mark_all_messaages (0.00s)
=== RUN TestMessageService_MarkAllMessages/should_print_error_and_return_empty_slice
network
TestMessageService_MarkAllMessages/should_print_error_and_return_empty_slice: message_test.go:53: PASS: Post(string,*services.MarkAllMessagesRequestPayload,*model.MarkAllMessagesResponse)
--- PASS: TestMessageService_MarkAllMessages/should_print_error_and_return_empty_slice (0.00s)
PASS
coverage: 5.6% of statements in ../../gqlgen-cnode/...
Process finished with exit code 0
I highly recommend to get familiar with the gomock framework and develop towards interfaces. What you need would look something like this.
// SetArg does the job
myObj.EXPECT().DoSomething(gomock.Any()).SetArg(0, <value you want to r eturn>).Return(nil)
https://github.com/golang/mock#building-mocks

How do I write unit test aws-sdk-go-v2 dynamodb implementation

I am still grasping go-interfaces and I can mock the WaitUntilTableExists func. But unable to mock PutItemRequest.
Here's my main.go snippet
func MyPutItem(d mydata, client dynamodbiface.DynamoDBAPI) error {
input := &dynamodb.PutItemInput{
....
}
req := client.PutItemRequest(input)
result, err := req.Send()
log.Println(result)
return err
}
main_test.go snippet
type mockDynamoDBClient struct {
dynamodbiface.DynamoDBAPI
}
func (m *mockDynamoDBClient) PutItemRequest(input *dynamodb.PutItemInput) dynamodb.PutItemRequest {
// Most probably this is where I need your help
}
func TestStoreInDynamoDB(t *testing.T) {
var mockClient = new(mockDynamoDBClient)
d := mydata{}
result := DynampDBPutItem(d, mockClient)
t.Log(result)
}
Taking your example, you could do your assertions directly in the mock
type mockDynamoDBClient struct {
t *testing.T
expected *dynamodb.PutItemInput
response *dynamodb.PutItemOutput
dynamodbiface.DynamoDBAPI
}
func (m *mockDynamoDBClient) PutItemRequest(input *dynamodb.PutItemInput) dynamodb.PutItemOutput {
// some kind of equality check
if !reflect.DeepEqual(m.expected, input) {
t.Errorf(...// some error message)
}
return m.response
}
The main problems with this example are:
t *testing.T, expected *dynamodb.PutItemInput and response response *dynamodb.PutItemOutput all need to be inside the struct which feels messy.
Instead you could use an anonymous function to do this:
type mockDynamoDBClient struct {
f func(input *dynmaodb.PutItemInput) *dynamodb.PutItemOutput
dynamodbiface.DynamoDBAPI
}
func (m *mockDynamoDBClient) PutItemRequest(input *dynamodb.PutItemInput) dynamodb.PutItemOutput {
return m.f(input)
}
Now in the test code you can make slightly better use of the mock struct:
m := &mockDynamoDBClient{
f: func(input *dynamodb.PutItemInput) *dynamodb.PutItemOutput {
// assertions on input
// return mock responses
}
}
EDIT based on comment:
You should also consider making your MyPutItem function dependent on the smallest interface possible. If you only need access to the PutItemRequest method then you can create your own interface for that method and use that in MyPutItem
type MyDynamoPutter interface {
func (c *DynamoDB) PutItemRequest(input *PutItemInput) PutItemRequest
}
Then in MyPutItem you can use your own interface:
func MyPutItem(d mydata, client MyDynamoPutter) error {
input := &dynamodb.PutItemInput{
....
}
req := client.PutItemRequest(input)
result, err := req.Send()
log.Println(result)
return err
}
This reduces the surface area that you need to mock!
Faking the SDK like this works:
main_test.go
type fakeDynamoDBClient struct {
dynamodbiface.DynamoDBAPI
}
func (m *fakeDynamoDBClient) GetItemRequest(input *dynamodb.GetItemInput) dynamodb.GetItemRequest {
return dynamodb.GetItemRequest{
Request: &aws.Request{
Data: &dynamodb.GetItemOutput{
Item: map[string]dynamodb.AttributeValue{
"count": dynamodb.AttributeValue{
N: aws.String("10"),
},
},
},
},
}
}
func (m *fakeDynamoDBClient) PutItemRequest(input *dynamodb.PutItemInput) dynamodb.PutItemRequest {
return dynamodb.PutItemRequest{
Request: &aws.Request{
Data: &dynamodb.PutItemOutput{},
},
}
}
func TestUpdateCount(t *testing.T) {
err := UpdateCount(10, &fakeDynamoDBClient{})
if err != nil {
t.Error("Failed to update badge count on dynamodb", err)
}
}
main.go
func UpdateCount(count int, client dynamodbiface.DynamoDBAPI) error {
...
}

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")
}
}

Init function breaking unit tests

In the package I want to test, I have an init function that loads the configuration file containing some stuff I want to use to run my application. However, I don't want to trigger this init function while running my unit tests.
Is there any way for skipping or preventing this init function to be called during the unit tests?
Some snippets to illustrate the question:
func init() {
var err error // Necessary to prevent config variable shadowing
config, err = loadConfig("./client/config.yml")
if err != nil {
log.Fatal(err)
}
}
func loadConfig(filepath string) (*Config, error) {
viper.SetConfigFile(filepath)
if err := viper.ReadInConfig(); err != nil {
return nil, fmt.Errorf("Error loading config file: %s", err)
}
(...)
}
// New returns a Config value(!)
func New() Config {
return *config
}
A test case:
func TestNew(t *testing.T) {
expected := &Config{}
observed := New()
if !reflect.DeepEqual(observed, expected) {
t.Errorf("observed %+v. expecting %+v\n", observed, expected)
}
}
I'm not sure whether there's a nicer way of doing this, but if you consider the fact that package-level variables are initialized before the init func is run you can use a flag to tell you whether you're running tests or not.
var _testing = false
func init() {
if _testing {
return
}
var err error // Necessary to prevent config variable shadowing
config, err = loadConfig("./client/config.yml")
if err != nil {
log.Fatal(err)
}
}
// ...
And in your test file you could do something like this:
// not nice but works
var _ = (func() interface{} {
_testing = true
return nil
}())
func TestNew(t *testing.T) {
expected := &Config{}
observed := New()
if !reflect.DeepEqual(observed, expected) {
t.Errorf("observed %+v. expecting %+v\n", observed, expected)
}
}
You can read more on the initialization order here: https://golang.org/ref/spec#Program_initialization_and_execution