Why we use interface for mocking methods Golang

Viewed 4461

I am new to Golang and have been exploring but not clear about mocking in unit tests. Can anyone explain following specific questions ?

Question1: For writing unit tests in Golang, why we need to have interfaces to mock methods, why not only struct ?

Question2: Why we inject the interface in struct(where we call external method)

With struct -

type GlobalData struct {}

var (
    GlobalObj = GlobalData{}
)

func (g GlobalData) GetGlobalData(a string) string{
    return a
}

With interface definition-

type GlobalInterface interface {
    GetGlobalData(a string) string
}

type GlobalData struct {}

var (
    GlobalObj = GlobalData{}
)

func (g GlobalData) GetGlobalData(a string) string{
    return a
}

Thanks

2 Answers

Question 1: For writing unit tests in Golang, why we need to have interfaces to mock methods, why not only struct ?

Answer: Its not mandatory

Question 2: Why we inject the interface in struct(where we call external method)

Answer: Because, it helps you to replace the actual function call (that might trigger some out of scope actions as a part of unit test , such as database call, some API call etc) by injecting a MockStruct (which will be implementing the same interface that is there in the actual code). Polymorphism in simple words.

So, you create a MockStruct and define your own mockMethods to it. As polymorphism, your unit test pick MockStruct without complaining. Calling actual DB or http endpoints do not come under unit testing.

Just for reference, I can point you to one of my github codebase where I wrote a small test case for a file. As you can see I mocked :

  1. GuestCartHandler interface , that allowed me to not call the actual implementation
  2. Mocked sql connection using "github.com/DATA-DOG/go-sqlmock" package. This helped me to avoid establishing actual db client (so, no dependency of database while unit testing)

Let me know if you get the idea conceptually or do you need some more clarification.

If you have methods on types in package user let's say, ex. package user

type User struct {
 name string
}

func (u *User) GetUserProfile() UserProfile{}

And now on import in catalog package :

package catalog

import user

func getUserCatalog(user user.User) []catalog {
 user.GetUserProfile()
}

Now to test getUserCatalog method there are 2 ways:

1. var getUserProfileFunc = user.GetUserProfile

using this approach mock can be easily passed at test run time like:

getUserProfile = func() UserProfile { 
 return fakeUserProfile 
}

this is the easiest way to test it.

Now there is another way using interface, in package user add an interface like

type UserInterface interface {
  GetUserProfile() UserProfile
}

if User package is a library on which you don't have control then create your own interface, type and use this.

In this case testing in catalog package will become like:

because now methods will be invoked from UserInterface type not from UserType, hence while testing :

UserInterface = fakeUserStruct

and follow below steps

//1. define type of func to return 

type typeGetUserProfile func() UserProfile

//2. create a var to return

var mockedGetUserProfile typeGetUserProfile

//3. create a type

type FakeUser struct{}

//4. implement method interface

func (user *FakeUserStruct) GetUserProfile() UserProfile{
  return mockedGetUserProfile
 }

now when running test :

mockerGetUserProfile = func() UserProfile {
  return fakeUserProfile
 }

There is mock library which helps in creating boilerplate code for mocking. Check this https://github.com/stretchr/testify

There are many other mock library, but I had used this one, this was really cool.

I hope this helps.

if not please let me know, i'll give some example code and push it to Github.

Also please check https://levelup.gitconnected.com/utilizing-the-power-of-interfaces-when-mocking-and-testing-external-apis-in-golang-1178b0db5a32

Related