Reputation: 3211
I want to mock a method of a nested struct. I have tried to define an interface and make the Mock implement it, but I could not get it working.
This is the struct I want to test:
type OuterThing struct {
innerThing *InnerThing
}
func (a *OuterThing) doLotsOfStuff() {
println("i am doing")
u, err := a.innerThing.DoStuff("lots of stuff")
if err != nil {
println("ran into an error, also doing some logic")
}
println("and more", u)
}
The nested struct, of which I want to mock its DoStuff()
function, looks like this:
type InnerThing struct {
name string
}
func (b *InnerThing) DoStuff(x string) (uint64, error) {
println("i want to mock this method")
return 0, nil
}
Little side twist: I can not change the code of these structs and their methods.
To make my point a bit more clear i have written this test:
func TestDoLotsOfStuff(t *testing.T) {
testCases := []struct {
name string
structUnderTest *OuterThing
}{
{
name: "happy case",
structUnderTest: &OuterThing{
innerThing: &InnerThing{name: "I am the inner thing."},
},
},
{
name: "error case",
structUnderTest: &OuterThing{
innerThing: &InnerThing{name: "i should be a mock with a mocked DoStuff function"},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
testCase.structUnderTest.doLotsOfStuff()
// assertions
})
}
}
I am quite familiar with Java and Mockito and this would be a pretty trivial task. I know Go has lots of differences with its implicit interfaces and no classes et cetera, but is this really such an uncommon usecase?
Upvotes: 0
Views: 1736
Reputation: 91
You can do it by using an Interface and Dependency Injection.
Let's say, you have an Interface called Inner
that contains the DoStuff()
method. The OuterThing
struct should contain this Interface instead of the struct InnerThing
. Now, you can use dependency injection to pass the Inner
Interface to this OuterThing
struct.
type Inner interface {
DoStuff(string) (uint64, error)
}
type OuterThing struct {
innerThing Inner
}
func NewOuterThing(in Inner) *OuterThing {
return &OuterThing{
innerThing: in,
}
}
Now, in the test, you can create the mockInnerThing
struct and implement the mock DoStuff()
method and pass the mockInnerThing
to the OuterThing
struct.
type MockInnerThing struct {
name string
}
func NewMockInnerThing(name string) *MockInnerThing {
return &MockInnerThing{
name: name,
}
}
func (b *MockInnerThing) DoStuff(x string) (uint64, error) {
println("Mock!")
return 0, nil
}
func TestDoLotsOfStuff(t *testing.T) {
mit := NewMockInnerThing("Test")
ot := NewOuterThing(mit)
ot.doLotsOfStuff()
}
Upvotes: 2