gogomock

Testing my interface in golang with mocks. Specifically test 1 function that calls a sibling function


Let's say I have this code and I want to create a test for Foo()

The important part it Foo makes a call to Bar

package main

type MyInterface interface {
    Foo() error
    Bar() error
}

type MyStruct struct {
}

func NewMyStruct() MyInterface{
    return &MyStruct{}
}

func (m *MyStruct) Foo() error {
    // do something
    m.Bar()
    // do something else
    return nil
}

func (m *MyStruct) Bar() error {
    // do something
    return nil
}

Is it possible to create a test for this where I can mock the behaviour of Bar before I run Foo? or am I doing something fundamentally wrong?

I can see that if I extracted Bar into its own service I would mock it that way but that also doesn't feel right.

Any insights or links to documentation would be great.


Solution

  • You should be able to achieve what you need with a couple of changes. First, let me present the code and then I'll walk you through all of the relevant changes.

    main.go file

    package main
    
    type MyInterface interface {
        Foo() error
        Bar() error
    }
    
    type MyStruct struct {
        DS MyInterface
    }
    
    // here you've to depend upon an interface and return a pointer to a struct
    func NewMyStruct(ds MyInterface) *MyStruct {
        return &MyStruct{
            DS: ds,
        }
    }
    
    func (m *MyStruct) Foo() error {
        // do something
        m.DS.Bar()
        // do something else
        return nil
    }
    
    func (m *MyStruct) Bar() error {
        // do something
        return nil
    }
    
    func main() {}
    

    Here, I changed a couple of things in order to be able to successfully mock our dependencies. Let me recap them:

    1. The MyStruct has a dependency of type MyInterface
    2. The function NewMyStruct accepts the interface as parameter and returns a pointer to the MyStruct struct
    3. In the Foo method, we're going to rely on the DS field of our pointer receiver type instance

    Now, let's switch to the test file.

    main_test.go file

    package main
    
    import (
        "testing"
    
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/mock"
    )
    
    // 1. declare mock
    type myStructMock struct {
        mock.Mock
    }
    
    // 2. implement the interface
    func (m *myStructMock) Foo() error {
        args := m.Called()
        return args.Error(0)
    }
    
    func (m *myStructMock) Bar() error {
        args := m.Called()
        return args.Error(0)
    }
    
    func TestFoo(t *testing.T) {
        // 3. instantiate/setup mock
        myStructMock := new(myStructMock)
        myStructMock.On("Bar").Return(nil).Times(1)
    
        sut := NewMyStruct(myStructMock)
        err := sut.Foo()
    
        // 4. check that all expectations were met on the mock
        assert.Nil(t, err)
        assert.True(t, myStructMock.AssertExpectations(t))
    }
    

    Here, I found it best to add comments within the code to give you a chronological order of what's going on. The idea is that the real system tested (e.g. the sut variable) is relying on mock instead of actual implementations. Thanks to the method Times, we make sure that the Bar method is called once.

    I always used this approach when it comes to testing production code and I find it pretty flexible and amazing!
    Let me know if this helps you or if you still need something else, thanks!