Is it possible to mock a function imported from a package in golang?

24,528

Solution 1

Yes, with a simple refactoring. Create a zSomeFunc variable of function type, initialized with z.SomeFunc, and have your package call that instead of z.SomeFunc():

var zSomeFunc = z.SomeFunc

func abc() {
    // ...
    v := zSomeFunc()
    // ...
}

In tests you may assign another function to zSomeFunc, one that is defined in tests, and does whatever the test wants it to.

For example:

func TestAbc(t *testing.T) {
    // Save current function and restore at the end:
    old := zSomeFunc
    defer func() { zSomeFunc = old }()

    zSomeFunc = func() int {
        // This will be called, do whatever you want to,
        // return whatever you want to
        return 1
    }

    // Call the tested function
    abc()

    // Check expected behavior
}

See related / possible duplicate: Testing os.Exit scenarios in Go with coverage information (coveralls.io/Goveralls)

Solution 2

One thing you can do is this:

import "x/y/z"

var someFunc = z.SomeFunc

func abc() {
    ...
    v := someFunc()
    ... 
}

And in your test file you would do this.

func Test_abc() {
    someFunc = mockFunc
    abc()
}

But make sure that you do this in a concurrent manner, if you have multiple TestXxx functions calling abc or setting someFunc you may be better of using a struct with a someFunc field.

Solution 3

Having function pointer and monkey patching it is the one of doing it. But then when you multiple functions to mock, you will have a number function pointers and unnecessarily you will have to call function using the function pointer only.

The better and the recommended idea to have an interface and make your function part of the struct implementing the interface. Once done that, you can generate mocks using some nice tools available for go.

I have been using this:

mockgen -source myModule.go -package myPackage -destination myModuleMock.go

You can install it by:

go get github.com/golang/mock
Share:
24,528
Pradeep
Author by

Pradeep

Updated on July 14, 2022

Comments

  • Pradeep
    Pradeep almost 2 years

    I have the following method to test, which uses a function imported from a package.

    import x.y.z
    
    func abc() {
        ...
        v := z.SomeFunc()
        ... 
    }
    

    Is it possible to mock SomeFunc() in golang?

  • Kaedys
    Kaedys about 7 years
    Worth noting, you can use the argument -p 1 to go test to force all tests to run in series rather than parallel.
  • Tareq Sha
    Tareq Sha over 4 years
    gmock is excellent but does not have a native solution for global functions. you have to wrap them with an interface.
  • inquisitive
    inquisitive over 4 years
    This article dev.to/jonfriesen/mocking-dependencies-in-go-1h4d explains the steps with examples for this approach.
  • user4002112
    user4002112 almost 4 years
    How to mock if the function returns more than one value?
  • icza
    icza almost 4 years
    @user4002112 The same way. Obviously the function used for mocking has to return multiple values too.
  • Taeyeong Jeong
    Taeyeong Jeong about 3 years
    Doesn't it have race condition problem when we run tests concurrently?
  • Ярослав Рахматуллин
    Ярослав Рахматуллин over 2 years
    This is essentially global state. May work for something trivial, but may just as probably cause serious pain to someone who inherits such code (i.e several hours lost debugging). This is not a good pattern in a professional setting.
  • Ярослав Рахматуллин
    Ярослав Рахматуллин over 2 years
    This is essentially global state. May work for something trivial, but may just as probably cause serious pain to someone who inherits such code (i.e several hours lost debugging). This is not a good pattern in a professional setting.
  • icza
    icza over 2 years
    @ЯрославРахматуллин I would agree if the variable's value would be changed during the lifetime of the app. But it is not, its sole purpose is to aid testing, and its value is only changed in tests, so this use does not fall under the global state category.
  • Ярослав Рахматуллин
    Ярослав Рахматуллин over 2 years
    I had to debug a case like that last week. The test failed when run with all other tests, but passed when run in isolation. Global state means any variable or symbol that's globally available and can change (it was changed by other tests), including func-vars. It's literally the definition of global state.
  • kubanczyk
    kubanczyk about 2 years
    @Kaedys For this answer the default flag go test -parallel 1 does make sense. The -p 1 is however not a synonym for that! The former is goroutine-level, the latter the pid-level.