0

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?

1 Answer 1

2

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()
}
Sign up to request clarification or add additional context in comments.

3 Comments

I get a compile-time error: Unresolved reference 'DoStuff' inside the doLotsOfStuff() method.
can you show me the code?
Ok, I checked the pointers again and the compile time error is gone. But I had to change the source code of the structs i did not want to. Thanks anyways

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.