简体   繁体   中英

Golang Unit Test Mock method of nested struct

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?

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()
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM