17

I am fairly new to golang and I am struggling with a simple task.

I have the following class in golang

type struct A {
}

func (s *A) GetFirst() {
    s.getSecond()
}

func (s *A) getSecond() {
    // do something
}

And I want to write some tests for it however for that I need to override getSecond(). I attempted to do the following in my test files

type Ai interface {
    getSecond()
}

type testA struct {
    A
}

func (s *testA) getSecond() {
     // do nothing
}

func TestA(t *testing.T) {
   a := &A{}
   t := &testA{A: a}
   t.GetFirst()  
}

The idea here is to expose A getSecond() method to an interface and override by using embedding however this does not seem to work. The test still calls the original implementation of getSecond() instead of my mocked one.

One solution would of course be to create a proper interface for A which contains getFirst() and getSecond() and then in the test create a struct implementing both where getFirst() call the original implementation and getSecond() a dummy however I feel this is cumbersome and not the correct way of doing things.

Another possibility would be to assign getSecond() in the real implementation to a variable and override the variable in test but I also feel it is a bit strange to do this just for a simple override.

Am I going all wrong about this? Is there any way simpler way to do this using golang?

Alexandre Thenorio
  • 2,288
  • 3
  • 31
  • 50
  • 1
    Why do you want to mock either method? What is the real reason? What works really well is: Construct an `A` (which is easy as you have access to all the gory internals), then check that `getFirst()` and `getSecond()` produce the desired result. Rinse and repeat. Look at the standard library how this is done. Maybe you are still under the influence of other languages and `getFirst()` uses `getSecond()` which makes you believe you have to mock `getSecond()`. No, you can control what `getSecond()` does via the state of `A`. And please: No getter is in Go (and not golang). – Volker Mar 16 '17 at 17:01
  • 2
    Actually those are not getters, I just chose to name those functions like that in the example :) The problem at hand here is that getFirst will call getSecond which performs stuff which will not work under a unit test situation. – Alexandre Thenorio Mar 16 '17 at 17:13
  • 1
    To be more precise. A has a baseUrl and getFirst() performs an http request however before doing that it needs to rewrite the url base domain to include subdomains depending on the type of request and this is what getSecond() does. This causes issues with httptest package as you end up with http://subdomain.127.0.0.1 – Alexandre Thenorio Mar 16 '17 at 17:16
  • Well, than split getFirst() into two parts `X()` and `Y()`. The `Y` part is the main action which does the HTTP request to a URL which is either a parameter to `Y(url *url.URL)` or a field of `A`. Now you can test that the whole "fetch some URL and process it" works. The `X` parts calls `getSecond()` and takes care of the produced URL. What `getFirst()` does is now nothing more than chaining X and Y, maybe stuffing the output of Y into X. If Y works properly (testable) and X works (also testable) your are done. – Volker Mar 16 '17 at 17:25
  • 1
    You are right, it would solve most of the issue but what about testing `getFirst()` to prevent some future developer from going in and adding logic which is not covered by the tests? It would be nice to be able to catch such scenario with a test – Alexandre Thenorio Mar 20 '17 at 16:27
  • Well, that's what integration tests, black block test or end-to-end tests are good for. – Volker Mar 20 '17 at 20:09
  • 1
    @Volker, it would be much more helpful to provide a proper answer below, rather than trying to answer within the comments section. – Ed J Sep 28 '22 at 17:58

4 Answers4

12

You can't really override methods in golang as per this answer. However, as you point out you can have a separate interface for the "getSecond method" and have one implementation in your test cases and one implementation in your actual code.

type s interface{ 
    getSecond()
}

type A struct{
    s
}

type a struct{

}

func (s *A) GetFirst() {
    s.getSecond()
}

func (s a) getSecond() {
    // do something
}

//Use a 
A{a{}}

Then in Test have a different implementation of 'a'

type ta struct {

}
func (s ta) getSecond() {
    // do nothing
}

A{ta{}}
Stéphane Bruckert
  • 21,706
  • 14
  • 92
  • 130
Benjamin Kadish
  • 1,472
  • 11
  • 20
  • Given the amount of data I supplied in my question I think this is very much an acceptable solution. So is what Volker posted in the comments below my questions if you can live with not testing a method which is used to glue the whole thing and does not add extra logic to it. – Alexandre Thenorio Mar 20 '17 at 16:31
  • 2
    This solution became cumbersome if you would like to mock a `getThird` function inside `getSecond`! – Cirelli94 Apr 29 '21 at 06:43
2

mockcompose (https://github.com/kelveny/mockcompose) was created by me to address exactly the problem.

Class in Go is not the first class citizen, for those who come from other language worlds (i.e. Java), lack of ability for mocking sibling methods sometimes bothers.

Say you have a class foo

package foo

type foo struct {
    name string
}

func (f *foo) Foo() string {
    if f.Bar() {
        return "Overriden with Bar"
    }

    return f.name
}

func (f *foo) Bar() bool {
    if f.name == "bar" {
        return true
    }

    return false
}

You want to test Foo() method, however, when Foo() calls Bar(), you want Bar() to be mocked.

With mockcompose, you can first configure go generate and let mockcompose generate plumbing things for you.

mocks.go

//go:generate mockcompose -n testFoo -c foo -real Foo -mock Bar
package foo

mockcompose will then generate code: mockc_testFoo_test.go

//
// CODE GENERATED AUTOMATICALLY WITH github.com/kelveny/mockcompose
// THIS FILE SHOULD NOT BE EDITED BY HAND
//
package foo

import (
    "github.com/stretchr/testify/mock"
)

type testFoo struct {
    foo
    mock.Mock
}

func (f *testFoo) Foo() string {
    if f.Bar() {
        return "Overriden with Bar"
    }
    return f.name
}

func (m *testFoo) Bar() bool {

    _mc_ret := m.Called()

    var _r0 bool

    if _rfn, ok := _mc_ret.Get(0).(func() bool); ok {
        _r0 = _rfn()
    } else {
        if _mc_ret.Get(0) != nil {
            _r0 = _mc_ret.Get(0).(bool)
        }
    }

    return _r0

}

Now, all you need to do is just to write your unit testing logic as you do in other languages (i.e. Java), for example:

func TestFoo(t *testing.T) {
    assert := require.New(t)

    fooObj := &testFoo{}

    // Mock sibling method Bar()
    fooObj.On("Bar").Return(false)

    s := fooObj.Foo()
    assert.True(s == "")
}

For details, please check out https://github.com/kelveny/mockcompose, mockcompose can also help you test regular functions with callouts to imported functions from other packages.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Kelven Yang
  • 31
  • 1
  • 2
  • There are no classes in go, which makes me suspicious of a library whose author thinks there are. – Adrian Apr 06 '23 at 15:42
1

I think there are at least two alternatives.

Use always a function field

When the function mocked is something not related to the struct I'm mocking, maybe a third party function, or something written from another component of my application. I will assign the real working one when initializing the service or the mocked one on the tests.

// service.go
type MyService struct {
    getRandomID func() string
}

type Car struct {
    ID   string
    Name string
}

func (s *MyService) NewCar() (*Car, error) {
    car := Car{
        ID:   s.getRandomID(),
        Name: "ThisCar",
    }

    return &car, nil
}


// service_test.go

func newIDsForTests() func() string {
    i := 0
    return func() string {
        i++
        return fmt.Sprintf("%024d", i)
    }
}

func TestNewCar(t *testing.T) {
    s := MyService{
        getRandomID: newIDsForTests(),
    }

    actual, err := s.NewCar()

    if err != nil {
        panic(err)
    }

    expected := Car{ID: "000000000000000000000001", Name: "ThisCar"}
    if *actual != expected {
        panic("cars don't match")
    }
}


The Go Playground working example

Use a function field only when mocking

When the function to be mocked is something really related to the struct I'm working with, that is part of this component. I will always use the real working, and assign a mock function when needed for the tests.

While I think this solution is quite ugly, I also think that is for sure easily to use and to maintain, while also let you unit test your code at 100%!

My idea is to add a field mockedGetSecond to the struct, and to set its value only in the tests where you want to mock the real getSecond. In the real implementation you have to add a check of course, that if this func isn't nil, it must be used.

This is probably not a good pattern, or something I would like to use often, but I think I'll will use it to mock a function that do a lot of logic (and a lot of db calls, and need various input, ...) and is often called in the functions in the same service.

// service.go

import (
    "fmt"
    "testing"
)

type MyService struct {
    mockedGetSecond func() (string, error)
}

func (s *MyService) GetFirst() error {
    secondVal, err := s.getSecond()
    if err != nil {
        return err
    }

    fmt.Println("getSecond returned: ", secondVal)

    return nil
}

func (s *MyService) getSecond() (string, error) {
    if s.mockedGetSecond != nil {
        return s.mockedGetSecond()
    }

    // very complex function

    return "real", nil
}

// service_test.go

func TestGetFirst(t *testing.T) {
    myService := MyService{
        mockedGetSecond: func() (string, error) {
            return "mocked", nil
        },
    }

    err := myService.GetFirst()
    if err != nil {
        panic(err)
    }
}

func TestGetSecond(t *testing.T) {
    myService := MyService{}

    actual, err := myService.getSecond()
    if err != nil {
        panic(err)
    }

    if actual != "real" {
        panic("I would expect 'real'")
    }
}

The Go Playground working example

=== RUN   TestGetFirst
getSecond returned:  mocked
--- PASS: TestGetFirst (0.00s)
=== RUN   TestGetSecond
--- PASS: TestGetSecond (0.00s)
PASS
Cirelli94
  • 1,714
  • 1
  • 15
  • 24
0

In case someone ends up here on a similar dive down the rabbit hole: I wanted to mock a function called by another function to ensure it's called x times under specific conditions, such as you would do with Jest for example. I wanted to keep the program code as test-agnostic as possible, and having had little success with receiver methods and overriding functions in interfaces I opted for simply passing a function reference as an argument. So if you aren't dead set on an OOP style approach:

// app.go
type SubDoer func(string) string

func SubDo(something string) string {
    ...
    return something
}

func DoHandler(somethings []Something) string {
    return Do(somethings, SubDo)
}

func Do(somethings []Something, subDoer SubDoer) string {
    ...
    subDoer(something)
    return somethingElse
}

The DoHandler() function gives us a wrapper to call Do() with the actual SubDo() implementation, and we can unit test Do() with a mocked SubDo() by skipping the handler function:

// app_test.go
type MockDoer struct {
    counter int
}

func (m *MockDoer) SubDo(string) string {
    m.counter++
    return "something"
}

// Calls the mocked function from Do()
func TestDoWithMockedSubDo(t *testing.T) {
    mock := &MockDoer{}
    ...
    assert.Equal(t, "something", Do(somethings, mock.SubDo))
    assert.Equal(t, mock.counter, xAmount)
}

// Calls DoHandler() which calls Do() with the unmocked SubDo()
func TestDo(t *testing.T) {
    ...
    assert.Equal(t, "something else", DoHandler(somethings))
}

// SubDo() is unaffected by the mocks
func TestSubDo(t *testing.T) {
    ...
    assert.Equal(t, "a thing", SubDo(something))
}

rionpy
  • 1
  • 1