Go mocking with interfaces for testing

Viewed 6770

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",
        },
    })
}
1 Answers

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.

Related