Hi I would like to test or Mock a certain function and return a Mock response for this. To demonstrate below is my code
Sample.go
package main
import (
"fmt"
log "github.com/sirupsen/logrus"
)
var connectDB = Connect
func Sample() {
config := NewConfig()
response := connectDB(config)
fmt.Println(response)
log.Info(response)
}
func Connect(config *Config) string {
return "Inside the connect"
}
And my test is like this
Sample_test.go
package main
import (
"testing"
)
func TestSample(t *testing.T) {
oldConnect := connectDB
connectDB := func(config *Config) string {
return "Mock response"
}
defer func() { connectDB = oldConnect }()
Sample()
}
So when running go test I was expecting to receive and output of Mock response but I'm still getting Inside the connect. Is there something I'm missing here?
#jrefior is correct, but I'd suggest to use interface for mocking. Of course, it's up to you, bet for me it is more clear, but more complicated code :)
// lack some fields :)
type Config struct {
}
// Use interface to call Connect method
type IConnection interface {
Connect(config *Config) string
}
// Real connection to DB
type Connection struct {
}
func (c Connection) Connect(config *Config) string {
return "Inside the connect"
}
// Mock connection
type MockConnection struct {
}
func (c MockConnection) Connect(config *Config) string {
return "Mock connection"
}
// Accepts interface to connect real or mock DB
func Sample(con IConnection) {
log.Println(con.Connect(nil))
}
func main() {
realConnection := Connection{}
Sample(realConnection)
mockConnection := MockConnection{}
Sample(mockConnection)
}
The use of a colon here creates a new function-scoped variable with the same name:
connectDB := func(config *Config) string {
return "Mock response"
}
Remove the colon to assign to the package variable.
Related
This is the code I am testing
func listObjects(cli *client.Client, options clientOptions) ([]BlobObjects, error) {
objects, err := cli.ListBlobObjects(...)
}
In my test setup I do this
type MockClient struct {
MockListBlobObjects func() ([]BlobObjects, error)
}
func (m *MockClient) ListBlobObjects(....) ([]BlobObjects, error) {
// return some mock response
}
And this is my test case
func TestBlobObjects(t *testing.T) {
tests := map[string]struct {
client *MockClient
...
}{
"Test case 1": {
client: &MockClient{
MockListBlobObjects: ....,
},
....
},
....
for testName, test := range tests {
blobs, err := (test.client, clientOptions{})
// make assertions here
}
The problem is test.client. Compiler is telling me
cannot use test.client (variable of type *MockClient) as *client.Client value in argument to listObjects
My hope was I have a mock client and if I call the function under test, then the mock client passed will call the mocked listObjects. This is how I would do in Python.
What should I do in Golang?
You need to create a common interface, which will be implemented by both types
type Client interface {
ListBlobObjects func() ([]BlobObjects, error)
}
type RealClient struct {
}
func (c *RealClient) ListBlobObjects(....) ([]BlobObjects, error) {
// return some response
}
type MockClient struct {
// note: don't put method signature in function body
}
func (m *MockClient) ListBlobObjects(....) ([]BlobObjects, error) {
// return some mock response
}
and then
var client client.Client
client = &RealClient{}
// or
client = &MockClient{}
Given:
// FileReader interface for reading from a file
type FileReader interface {
ReadFile(filename string) ([]byte, error)
}
type FileRead struct {}
// ReadFile reads from filename fn using ioutilReadFile
func (fr FileRead) ReadFile(fn string) ([]byte, error) {
return ioutil.ReadFile(fn)
}
type Dev struct {
*FileRead
}
func NewDev() *Dev {
frd := FileRead{}
return &Dev{frd}
}
// Function that does some job
func (dev Dev) DoSomeStuff() {
//...
dev.ReadFile("file")
//...
}
func main () {
doer := NewDev()
doer.DoSomeStuff()
}
During unit testing, the ReadFile operation should be mocked. How can one best achieve this in go test?
Dev struct could instead use FileReader interface, but then struct embedding is no longer used and the syntax in DoSomeStuff becomes less obvious.
If you are using a DI framework such as Dargo you can inject something that implements FileReader into dev. In your main-line code you would bind the normal FileReader, but in your test you can use a mock FileReader. The rest of your code should not know the difference. It would look something like this:
import (
"github.com/jwells131313/dargo/ioc"
"io/ioutil"
"testing"
)
type FileReader interface {
ReadFile(filename string) ([]byte, error)
}
type FileRead struct{}
// ReadFile reads from filename fn using ioutilReadFile
func (fr FileRead) ReadFile(fn string) ([]byte, error) {
return ioutil.ReadFile(fn)
}
type Dev struct {
MyReader FileReader `inject:"FileReader"`
}
/* Not here anymore, but you can implement DargoInitializer
if you need more initialization of Dev
func NewDev() *Dev {
frd := FileRead{}
return &Dev{frd}
}
*/
// Function that does some job
func (dev Dev) DoSomeStuff() {
//...
dev.MyReader.ReadFile("file")
//...
}
var locator ioc.ServiceLocator
func initialize() error {
l, err := ioc.CreateAndBind("Example", func(binder ioc.Binder) error {
binder.Bind("Dev", &Dev{})
binder.Bind("FileReader", &FileRead{})
return nil
})
locator = l
return err
}
func main() {
initialize()
raw, _ := locator.GetDService("Dev")
doer := raw.(*Dev)
doer.DoSomeStuff()
}
// Here is test code
type TestFileRead struct{}
// ReadFile is a mock that just returns a zero-length byte array
func (tfr TestFileRead) ReadFile(fn string) ([]byte, error) {
return []byte{}, nil
}
func TestMe(t *testing.T) {
initialize()
ioc.BindIntoLocator(locator, func(binder ioc.Binder) error {
binder.Bind("FileReader", &TestFileRead{}).Ranked(1)
return nil
})
// Test Me!
}
In the above example the "normal" file reader is injected in the normal code, but in the test there is a TestFileReader with a higher rank. So when you went to get Dev in the test it would be injected with the test one, not the one from the main-line code.
Hope this helps.
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 {
...
}
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")
}
}
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")
}
}