4

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

Siyaram Malav
  • 4,414
  • 2
  • 31
  • 31
  • 2
    A very large portion of unit testing can (and is) done without introducing interfaces and mocks. I do not know were you got that information. Take a look at the unit tests in the stdlib for real-world best testing practice in Go. – Volker Aug 12 '20 at 14:04
  • 3
    If you want to use a mock, you have to use an interface in practically every language. The interface is the type that you can substitute with either a mock in your tests, or a real implementation in production. It's a fundamental quality of mocking. – Adrian Aug 12 '20 at 14:05
  • @Volker , thanks, can you provide any reference ? Because my exploration led me to use interfaces for mocking the methods. – Siyaram Malav Aug 13 '20 at 16:33
  • @Adrian , Thank you for information. can you please explain the logic behind it, cant we use struct for mocking ? – Siyaram Malav Aug 13 '20 at 16:36
  • 1
    A struct is an implementation. To be able to use two different implementations (a mock a and a real implementation) interchangeably, you have to use an interface. – Adrian Aug 13 '20 at 16:59
  • @SiyaramMalav I _gave_ a reference: The stdlib. Just take a look. – Volker Aug 14 '20 at 05:15
  • @SiyaramMalav: Did you check my answer for why we use `interfaces` ? Let me know if you need more clarification for an answer. I have also shared some code. – Shashank Vivek Aug 15 '20 at 13:22
  • @ShashankVivek , i have commented in reply of your answer.Please see – Siyaram Malav Aug 16 '20 at 13:35

2 Answers2

5

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.

Shashank Vivek
  • 16,888
  • 8
  • 62
  • 104
  • Hey @Shashank , Thanks for answering and nice reference/structure of code. Here what i see, please correct me if am wrong 1. handler package is dependent on "dao" package.and we have injected "GuestCartHandler" interface in "guestCartItemsImpl" struct from handler package. The primary purpose of injection is to mock the methods from dao package while writing the unit tests fir handler package's methods right !!. 2.But why interface for achieving this ? if this one has to be the way then i just want to understand the logic behind it.Every where i see example with interfaces of unit tests. – Siyaram Malav Aug 16 '20 at 13:35
  • @SiyaramMalav: Did you read about the Polymorphism as suggested in my answer? "By creating interface, you are able to swap between `actual` and `mock` function" . interface helps you define `SomeType` , and `mock` become that `SomeType` once it implements those methods. Hence the code does not complain when you swap it in unit test. Please read this double quoted line and let me know if that helped – Shashank Vivek Aug 16 '20 at 16:27
  • Hi @Shashank , i am not able to relate Polymorphism to this context as Polymorphism talks about having different implementation of method with same method name but different number of parameters/ parameters types. But here, the parameters are the same in actual code and unit tests. – Siyaram Malav Aug 17 '20 at 12:29
  • 1
    @SiyaramMalav: `In computer science, it describes the concept that objects of different types can be accessed through the same interface.` . Taken from https://stackify.com/oop-concept-polymorphism/ . Since Go does not support inheritance , maybe https://stackoverflow.com/questions/38123911/golang-method-override will also help you understand the context which mentioned. Remember, The only reason is to `replace actual method with mock ones, without go compiler complaining about it` . Does this help ? I also struggled during my initial days so I can understand your situation :) – Shashank Vivek Aug 17 '20 at 13:57
  • @ShshankVivek,yup thanks :) I do have to ask these as you seems more familiar of these go stuff. 1. I was looking at this- https://github.com/shashankvivek/e-food-server/blob/5a9ba29511dd9ddb13bf472ed083f9313392653f/api/models/billable_cart.go#L41 If i write the unit test for " Validate" method then is use of interface way of mocking the method like "validateItems" and "validateOfferItems" ? or any other best way to do it !!! Because i have got the use case that in a "base" package(there are multiple files) , we have to mock the methods. – Siyaram Malav Aug 17 '20 at 17:10
  • @SiyaramMalav : I dont think you should create `interface` to test `Validate`. You can prepare a dummy `BillableCart` inside test file and then simply call `Validate` or `validateItems` . While calling `Validate` you can pass respective parameters (which in this case is `strfmt.Registry`) . If you drill down on `Registry` , you'll see that its an `interface` https://godoc.org/github.com/go-openapi/strfmt#Registry which can allow someone to mock (as it is an opensource API for the world) if required. Does that make sense ? – Shashank Vivek Aug 18 '20 at 05:42
  • @Shshankvivek, yes that makes sense to me. but i have got the model methods in place of "Validate" and "validateItems" in the same package. So i need to mock them.The Difference is , its in same package and earlier we talked the external package library and mocking. Specifically i am talking about sqlboiler (and ORM generator for go) if you have got idea about that !!! i have my own model methods which uses internally sqlboiler methods. – Siyaram Malav Aug 18 '20 at 16:31
  • @SiyaramMalav: Can you paste another question with the code because it's difficult to provide any solution without code . that would help me answer it . If this answer which I have provided helped you, please mark it as an answer and provide me link of another question. Cheers ! – Shashank Vivek Aug 18 '20 at 16:35
  • sure thanks.@Shashankvivek. Question link - https://stackoverflow.com/questions/63480510/how-to-mock-methods-in-same-package-golang-unit-tests – Siyaram Malav Aug 19 '20 at 05:27
-1

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

Allah-The-Dev
  • 57
  • 2
  • 8
  • Helpful links. Another link https://quii.gitbook.io/learn-go-with-tests/go-fundamentals/mocking –  Aug 12 '20 at 22:18