Search code examples
unit-testinggogo-testing

Go mocking with interfaces for testing


I'm pretty new with Go, and I'm coming from OOP languages. Now the concept seems quite different in go of interfaces and classes.

I was wondering how mocking would work in case of testing. The confusion I'm having is whether ok to use struct as a classes and if the approach below is how you suppose to do? Assuming that DefaultArticlesRepository would be for real data and MockArticlesRepository for mocking it.

type ArticlesRepository interface {
    GetArticleSections() []ArticleSectionResponse
}

type DefaultArticlesRepository struct{}
type MockArticlesRepository struct{}

func (repository DefaultArticlesRepository) GetArticleSections() []ArticleSectionResponse {
    return []ArticleSectionResponse{
        {
            Title: "Default response",
            Tag:   "Default Tag",
        },
    }
}

func (repository MockArticlesRepository) GetArticleSections() []ArticleSectionResponse {
    return []ArticleSectionResponse{
        {
            Title: "Mock response",
            Tag:   "Mock Tag",
        },
    }
}

func ArticleSectionsProvider(v ArticlesRepository) ArticlesRepository {
    return v
}

func TestFoo(t *testing.T) {
    realProvider := ArticleSectionsProvider(DefaultArticlesRepository{})
    mockProvider := ArticleSectionsProvider(MockArticlesRepository{})

    assert.Equal(t, realProvider.GetArticleSections(), []ArticleSectionResponse{
        {
            Title: "Default response",
            Tag:   "Default Tag",
        },
    })

    assert.Equal(t, mockProvider.GetArticleSections(), []ArticleSectionResponse{
        {
            Title: "Mock response",
            Tag:   "Mock Tag",
        },
    })
}

Solution

  • Firstly, I suggest you to use https://github.com/vektra/mockery for generating mock structs automatically based on interfaces. Implementing a mock struct like your is ok but I think it just wastes your time and effort if you do not really need a very special behavior for that struct.

    Secondly, we do not need to test mock structs like you do in your code.

    assert.Equal(t, mockProvider.GetArticleSections(), []ArticleSectionResponse{
        {
            Title: "Mock response",
            Tag:   "Mock Tag",
        },
    })
    

    So when we use mock structs, suppose struct a is a dependency of struct b. For example:

    type A interface {
        DoTask() bool
    } 
    
    type a struct {}
    
    func (sa *a) DoTask() bool {
        return true
    }
    
    type b struct {
        a A
    }
    
    func (sb *b) DoSomething() bool {
        //Do some logic
        sb.a.DoTask();
        //Do some logic
        return true;
    }
    

    And you want to test function DoSomething of struct b. Of course you do not care and do not want to test function DoTask of struct a in this case. Then you just simply provide a mock of struct a to struct b in the test. This mock also helps you avoid to deal with any struggle related to struct a in testing struct b. Now your test should be like this:

    func (s *TestSuiteOfStructB) TestDoSomething_NoError() {
        //Suppose that mockedOfA  is a mock of struct a
        instanceOfB := b{a: mockedOfA}
        mockedOfA.On("DoTask").Return(true)
        actualResult := instanceOfB.DoSomething()
        s.Equal(true, actualResult)
    }
    

    The last, this is just a small thing but do not see a clear responsibility of your ArticleSectionsProvider.