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

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?



Solution 1:[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:[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:[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

Solution 4:[4]

mockcompose uses an approach that allows you to generate a mocking class, you can direct mockcompose to include your selected dependency closure (any imported functions from other packages). In the mean time, it generates a cloned copy of your subject function with local overrides so that you can test against. You can embed code generation process with go generate, therefore ensure your cloned copy is always in-sync with your code changes.

Say you have a function functionThatUsesGlobalFunction that imports Sprintf in package fmt.

func functionThatUsesGlobalFunction(
    format string,
    args ...interface{},
) string {
    //
    // skip fansy logic...
    //

    // call out to a global function in fmt package
    return fmt.Sprintf(format, args...)
}

Your goal is to test functionThatUsesGlobalFunction with Sprintf in package fmt being mocked.

To do that, you can configure go generate with mockcompose as following:

mocks.go

//go:generate mockcompose -n mockFmt -p fmt -mock Sprintf
//go:generate mockcompose -n mockJson -p encoding/json -mock Marshal
//go:generate mockcompose -n clonedFuncs -real "functionThatUsesGlobalFunction,fmt=fmtMock"
package clonefn

go generate mockcompose will then generate plumbing classes for you, enable you to write test code as following:

package clonefn

import (
    "testing"

    "github.com/stretchr/testify/mock"
    "github.com/stretchr/testify/require"
)

var fmtMock *mockFmt = &mockFmt{}

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

    // setup function mocks
    fmtMock.On("Sprintf", mock.Anything, mock.Anything).Return("mocked Sprintf")

    // inside functionThatUsesMultileGlobalFunctions: fmt.Sprintf is mocked
    assert.True(functionThatUsesGlobalFunction_clone("format", "value") == "mocked Sprintf")
}

Please checkout this for details.

Solution 5:[5]

While creating a package level variable is a viable option, it comes with some restrictions. to name a few:

  1. It discourages running parallel tests using t.Parallel() as there can be race conditions on different behaviors of mocked function.
  2. it is dangerous as a future developer in the same package can accidentally update the implementation of this global variable.

another way is to pass along the methods you want to mock as arguments to the function to enable testability. In my case, I already had numerous clients calling this method and thus, I wanted to avoid violating the existing contracts. so, I ended up creating a wrapped function.

eg:

import (
 z "x.y.z"
)

//this should replicate the type of function z from x.y.z
type operation func() resp

func wrappedAbc(op operation) {
  ....
  resp := op()
  ....
}

func Abc() {
  wrappedAbc(z)
}

now for testing the actual logic, you will test calls to wrappedAbc instead of abc and you would pass it a mocked operation. this will allow you to test all the business logic while not violating API contract with current clients of method Abc.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2
Solution 3 Tats_innit
Solution 4 tomerpacific
Solution 5 AppleCiderGuy