5

I'm trying to unit test a receiver function that calls other receiver functions in that struct.

Let's say I want to test Three() and mock the call to two() in the following:

type MyStruct struct {
    a string
    b string
}

func (m *MyStruct) one() int {
    return 2
}

func (m *MyStruct) two() int {
    return m.one() * 2
}

func (m *MyStruct) Three() int {
    return m.two() * 2
}

I was following method two of the following answer.

I created a custom constructor for every single function that I wanted to unit test and overrode those methods with mocked versions. But I thought it may not be easy to maintain the code once the number of functions grow.

Is there any preferred way of mocking such functions? I wish the official documentation had some guidelines on how to mock things in different scenarios, similar to what mox on Python provides.

Also, note that I don't want to use a third party mocking library.

Community
  • 1
  • 1
mohi666
  • 6,842
  • 9
  • 45
  • 51
  • 1
    Why bother? I would think a unit test per method is more than enough. Think of a unit test on method "Three" like a mini integration test. – jmaloney Mar 25 '15 at 04:44
  • That's exactly what I'm trying to do. I want to mock all those calls, because I'm only testing the functionality of a particular function. Function "Three" could be just any function here. It's not necessarily an integrated test. – mohi666 Mar 25 '15 at 08:02
  • 1
    Why *I don't want to use a third party mocking library*? `unittest.mock` was be a third party library till python3.2 and in python2.7 you need always install it by `pip`. – Michele d'Amico Mar 25 '15 at 08:42
  • Here's another approach with a full example that should make clear how it is possible to write testable code in Go: https://stackoverflow.com/a/48206430/828366 – Francesco Casula Jan 11 '18 at 14:23
  • Possible duplicate of [Mock functions in Go](https://stackoverflow.com/questions/19167970/mock-functions-in-go) – Francesco Casula Jan 11 '18 at 14:24

2 Answers2

9

That is a really un-idiomatic way to test your stuff. All this mocking might be needed in other languages, but please don't do it in Go.

The natural way to test your code in the example you gave would be: 1) Write a table driven test for MyStruct.one and make sure you test all cases. Now that you know one works perfectly fine 2) do the same with MyStruct.two. Note that testing unexported stuff is possible, useful and common in Go. Now there is no longer a need need to mock some methods, just 3) write some table driven test for MyStruct.Three and check it works.

But maybe your methods one and two do fancier stuff, and access the environment (filesystem, database, network) and you do not want your tests of Three to depend on that? So refactor your code! Maybe Three should not be a method of MyStruct but a function which takes an interface OneAndTwoer as an argument and your production code calls Three with "real" MyStructs while your testcode calls it with InMemoryMyStrcuts which do not depend on the environment? You could call it a mock, I'd call it a different implementation of an interface.

In your example it is simple to give advice: Use table driven tests for one, two and Three and do not mock. For a more realistic problem the advice might be different but it is hard to give a general advice without knowing the circumstances. Best general advice is: Take a look at the test in the standard library where you'll find useful patterns for almost every testing scenario.

Volker
  • 40,468
  • 7
  • 81
  • 87
  • Assume `one` does a network call in this case how do you write test for `two` ? – Siddharth Mar 07 '20 at 16:43
  • @Siddhart Set up a test server with net/http/httptest.Server and let `one` do the "network call". I know: Some school teach "Unit-Test == does not access anything, no file system, no database, no network, no nothing". This is academic circlejerk and as such it is fine. In real life: How often do you run your unit tests on a system where no file system is available or you cannot call a local HTTP server? – Volker Mar 07 '20 at 18:02
2

You can do a refactor to pass two as a func in Three, so that you can directly mock the input.

func main() {
    var ms MyStruct
    fmt.Println(ms.one())
    fmt.Println(ms.two())
    fmt.Println(ms.Three(ms.two))
}

type MyStruct struct {}

func (m *MyStruct) one() int {
    return 2
}

func (m *MyStruct) two() int {
    return m.one() * 2
}

func (m *MyStruct) Three(two func() int) int {
    return two() * 2
}
codermomo
  • 23
  • 1
  • 1
  • 6