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.
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.
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
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 {
...
}
For my tests purpose, I would like to mock random numbers in Go. So I created the Random interface. During unit testing I return the identity function, while for implementation I generate a random number with rand package.
It is the right way for mocking random numbers in Go? Any help appreciated.
Go Playground: https://play.golang.org/p/bibNnmY2t1g
main:
package main
import (
"time"
"math/rand"
"fmt"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
type Random interface {
Uint(_ uint) uint
}
type rndGenerator func(n uint) uint
type Randomizer struct {
fn rndGenerator
}
func NewRandomizer(fn rndGenerator) *Randomizer {
return &Randomizer{fn: fn}
}
func (r *Randomizer) Uint(n uint) uint {
return r.fn(n)
}
func fakeRand(n uint) uint { return n }
func realRand(_ uint) uint { return uint(rand.Uint64()) }
func main() {
fakeRnd := NewRandomizer(fakeRand).Uint
fmt.Println(fakeRnd(1))
fmt.Println(fakeRnd(2))
realRnd := NewRandomizer(realRand).Uint
fmt.Println(realRnd(0))
fmt.Println(realRnd(0))
}
test:
package main
import (
"testing"
"math/rand"
"reflect"
)
func TestNewRandomizer(t *testing.T) {
fn := func(n uint) uint { return n }
type args struct {
fn rndGenerator
}
tests := []struct {
name string
args args
want *Randomizer
}{
{
"test",
args{fn},
&Randomizer{fn},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewRandomizer(tt.args.fn); reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
t.Errorf("NewRandomizer() = %v, want %v", got, tt.want)
}
})
}
}
func TestRandomer_Uint(t *testing.T) {
rnd := uint(rand.Uint64())
type fields struct {
fn rndGenerator
}
type args struct {
n uint
}
tests := []struct {
name string
fields fields
args args
want uint
}{
{
"test",
fields{func(n uint) uint { return n }},
args{rnd},
rnd,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Randomizer{
fn: tt.fields.fn,
}
if got := r.Uint(tt.args.n); got != tt.want {
t.Errorf("Randomizer.Uint() = %v, want %v", got, tt.want)
}
})
}
}
func Test_fakeRand(t *testing.T) {
rnd := uint(rand.Uint64())
type args struct {
n uint
}
tests := []struct {
name string
args args
want uint
}{
{
"test",
args{rnd},
rnd,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := fakeRand(tt.args.n); got != tt.want {
t.Errorf("fakeRand() = %v, want %v", got, tt.want)
}
})
}
}
func Test_realRand(t *testing.T) {
type args struct {
in0 uint
}
tests := []struct {
name string
args args
want bool
}{
{
"test",
args{0},
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := realRand(tt.args.in0); got < 1 {
t.Errorf("realRand() = %v, want %v", got, tt.want)
}
})
}
}
Your example makes no real use of the Random interface since the mocking is being done at the function field level of the Randomizer type.
I would recommend, if possible, to ditch the function field as well as the functions and instead define two separate implementations of the Random interface. You can use empty structs for this, they may look weird at first but they have the nice property of taking up 0 bytes of memory.
The main reason for the recommendation is that when you use function fields you lose the ability to compare your struct type values with reflect.DeepEqual. This is because two function values are only equal if they have the same type and both are nil.
As an example let's first look at your TestNewRandomizer which is a symptom of the issue stated above. In the test you're just comparing the type of the return value which is something already ensured by the compiler, so the test is absolutely pointless.
Now, let's say you drop the test, since it's useless, but for some reason you keep the function field. Because of this any struct type that depends on *Randomizer will also be untestable with DeepEqual and you'll have the same difficulties when trying to come up with a test for that type.
package main
import (
"time"
"math/rand"
"fmt"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
type Random interface {
Uint(_ uint) uint
}
type Randomizer struct {}
func NewRandomizer() Randomizer {
return Randomizer{}
}
func (r Randomizer) Uint(n uint) uint {
return uint(rand.Uint64())
}
type FakeRandomizer struct {}
func NewFakeRandomizer() FakeRandomizer {
return FakeRandomizer{}
}
func (r FakeRandomizer) Uint(n uint) uint {
return n
}
func main() {
fakeRnd := NewFakeRandomizer().Uint
fmt.Println(fakeRnd(1))
fmt.Println(fakeRnd(2))
realRnd := NewRandomizer().Uint
fmt.Println(realRnd(0))
fmt.Println(realRnd(0))
}
Note that i'm intentionally returning values instead of pointers since empty structs are smaller than pointers.
I have a method which generates a random integer and returns true if the integer is less than or equal to 50 and false if the integer is greater than 50 in the range [0, 100).
This is how I have created my structures to mock the functionality:
type Decider struct {
RandImpl func(int) int
}
func (d *Decider) Decide(randRange int) bool {
randVal := d.RandImpl(randRange)
log.Info("RandVal: ", randVal)
if randVal <= 50 {
return true
}
return false
}
I'm calling this method in this manner:
rand.Seed(time.Now().UnixNano())
decider := decide.Decider{
RandImpl: func(x int) int { return rand.Intn(x) },
}
decider.Decide(100)
In my _test.go file, I have this:
decider := Decider{
RandImpl: func(x int) int { return 42 },
}
ret := decider.Decide(100)
assert.True(t, ret)
This way you can mock the functionality of the random number generator.