Spying on methods in Golang - unit-testing

Is there any way to spy on methods in Golang?
For example, suppose I have
type Object struct {
A int
B string
C *interface{}
}
func (o *Object) Something(val interface{}) {
o.A = 102
// some other business logic under test
o.SomethingElse(o.C, val)
}
//...
func (o *Object) Process(val interface{}) interface{} {
// some business logic
return 43 // or something else. no me importa ya
}
//...
func (o *Object) SomethingElse(iPtr *interface{}, val interface{}) {
processedVal := o.Process(val)
if iPtr == nil {
iPtr = new(interface{})
}
*iPtr = val
}
In writing a test against Object.Something, we should not need to care about what's happening in SomethingElse or the Process invoked from within. Is there any way, in Golang, to isolate these dependencies?

My understanding of spying is that a single method of an object is faked or mocked while others are not. That's not possible in Go. But you can fake or mock your entire dependencies using interfaces.
In order to fake the SomethingElse method you would have to put it on another struct separate from your Object struct. The Object struct would also need an interface with the SomethingElse method.
type Object struct {
A int
elser Elser
}
func (o *Object) Something() {
// o.A = 102
o.elser.SomethingElse()
}
type Elser interface {
SomethingElse()
}
Now you can test the Object behavior by creating a fake that implements the SomethingElse method:
type fakeElser struct {
somethingElseWasCalled bool
}
func (f *fakeElser) SomethingElse() {
f.somethingElseWasCalled = true
}
func TestSomething(t *testing.T) {
fake := &fakeElser{}
o := &Object{
elser: fake,
}
o.Something()
if o.A != 102 {
t.Errorf("expected A to be %d", 102)
}
if !fake.somethingElseWasCalled {
t.Error("expected SomethingElse to be called")
}
}
https://play.golang.org/p/RdmbM6Sj5qU

Related

Passing mock as argument in Golang test

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{}

Ginkgo: Mocking a method for unit test

Please note that struct S implements the interface I.
I'm trying to test MethodA by mocking the response from MethodB.
sample.go:
package service
// This is implemented by S
type I interface {
MethodA(num int) int
MethodB(num int) int
}
type S struct {
num int
str string
}
func NewI(num int, str string) I {
return S{num, str}
}
func (s S) MethodA(num int) int {
resp := s.MethodB(num) // want to mock this
return 5 * resp
}
func (s S) MethodB(num int) int {
return num * 10
}
sample_test.go :
package service
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type MockI struct {
MockMethodA func(num int) int
MockMethodB func(num int) int
}
func (m *MockI) MethodA(num int) int {
return m.MockMethodA(num)
}
func (m *MockI) MethodB(num int) int {
return m.MockMethodB(num)
}
var _ = Describe("MethodA", func() {
Context("MethodA", func() {
Describe("normal case", func() {
It("should give proper response", func() {
i := NewI(1, "test")
// have to mock methodB()
// something like this:
// i.MethodB = MethodB(num int) int{
// return <some_value>
// }
got := i.MethodA(10)
expected := 500
Expect(got).To(Equal(expected))
})
})
})
})
Any help will be appreciated. Thanks!
Wouldnt the usage of dependency injection work? Inject the MockI into the code that you will be testing.
func funcTobeTested(i I) {
i.MethodA(0)
}
And then on the test:
mockI := MockI{}
// Will run the mock implementation of MethodA
funcTobeTested(mockI)
I think you can use the mockery package to mock the interface and use the .On function of mock package to mock just methodB in testing methodA.

Mock asserting into callbacks

I'm having the following problem when unit testing (working code here]
Assuming we have this:
var (
userDomain UserInterface
Tx TxInterface
)
type UserInterface interface {
Get() (*User, error)
}
type TxInterface interface {
Exec(fn func() error) error
}
type User struct {
ID int64
}
func (u *User) Get() (*User, error) {
return &User{
ID: 1,
}, nil
}
type WithTx struct{}
func (t *WithTx) Exec(fn func() error) error {
/* more logic here */
return fn()
}
Assuming we have the following 2 services:
func GetByID() (*User, error) {
user, err := userDomain.Get()
return user, err
}
func GetByIDWithTx() (*User, error) {
u := &User{}
/** Notice this implementation **/
if err := Tx.Exec(func() error {
user, _ := userDomain.Get()
u = user
return nil
}); err != nil {
log.Print("Tx err", err)
}
return u, nil
}
They are the same but once goes directly to the Get method, the other one runs into a callback fn. So far so go, both works like a charm. Again code is here
I'm trying to mock the userDomain and Tx for unit tests, so my mocks look like this:
var (
getUserFromMock func() (*User, error)
getFromMock func() error
)
type userMock struct{}
func (u *userMock) Get() (*User, error) {
return getUserFromMock()
}
type txMock struct{}
func (t *txMock) Exec(fn func() error) error {
return getFromMock()
}
When testing GetByID I have no problem whatsoever.
func TestGetByID(t *testing.T) {
userDomain = &userMock{}
Tx = &txMock{}
getUserFromMock = func() (*User, error) {
return &User{ID: 3}, nil
}
user, err := GetByID()
if user.ID != 3 {
t.Fatalf("error")
}
if err != nil {
t.Fatalf("error")
}
}
However, When I try to testing GetByIDWithTx, I cannot assert the mock values:
func TestGetByIDWithTx(t *testing.T) {
userDomain = &userMock{}
Tx = &txMock{}
getUserFromMock = func() (*User, error) {
return &User{ID: 4}, nil
}
/**** Mocking callback response ****/
getFromMock = func() error {
return nil
}
user, err := GetByIDWithTx()
if user.ID != 4 {
t.Fatalf("error") /** It fails here **/
}
if err != nil {
t.Fatalf("error")
}
}
I assume, at the moment I'm trying to assert the callback is still running in another routine but I'm just speculating, I don't have any idea what is going on here. I rather not use any library for a spy, I just want to understand how to solve this. Thoughts?
Go PlayGround HERE
You are never calling the fn given to the txmock. Change txMock.Exec to:
type txMock struct{}
func (t *txMock) Exec(fn func() error) error {
return fn()
// return getFromMock()
}
and remove getFromMock() function completely. It will then call getUserFromMock function to get the mocked user.

Mock embedded struct in go test

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.

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 {
...
}