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 Go?
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)
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.
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
While creating a package level variable is a viable option, it comes with some restrictions. to name a few:
It discourages running parallel tests using t.Parallel() as there can be race conditions on different behaviors of mocked function.
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.
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.
Related
I am trying to mock the Go function os.GetEnv() in my test files so that I can get the desired value for a particular environment variable.
For example I have defined.
abc := os.GetEnv("XYZ_URL")
Here I should be able to get the needed value for the variable abc. Also I have several places with the GetEnv functions.
It will be really helpful if someone can give me a workaround without the help of any Go framework.
First, you can't mock that function. You can only mock something exposed as an interface.
Second, you probably don't need to. Mocks, broadly speaking, are over-used, and should be avoided whenever possible.
When testing environment variables, you have few options.
If you're using Go 1.17 or newer, you can take advantage of the new Setenv function, which sets the environment variable for the duration of the current test only:
func TestFoo(t *testing.T) {
t.Setenv("XYZ_URL", "http://example.com")
/* do your tests here */
}
For older versions of Go, consider these options:
Create an object which can be mocked/doubled, which exposes the necessary functionality. Example:
type OS interface {
Getenv(string) string
}
type defaultOS struct{}
func (defaultOS) Getenv(key string) string {
return os.Getenv(key)
}
// Then in your code, replace `os.Getenv()` with:
myos := defaultOS{}
value := myos.Getenv("XYZ_URL")
And in your tests, create a custom implementation that satisfies the interface, but provides the values you need for testing.
This approach is useful for some things (like wrapping the time package), but is probably a bad approach for os.Getenv.
Make your functions not depend on os.Getenv, and instead just pass the value in. Example, instead of:
func connect() (*DB, error) {
db, err := sql.Connect(os.Getenv("XYZ_URL"), ...)
/* ... */
return db, err
}
use:
func connect(url string) (*DB, error) {
db, err := sql.Connect(url, ...)
/* ... */
return db, err
}
In a sense, this only "moves" the problem--you may still want to test the caller, which uses os.Getenv(), but you can at least reduce the surface area of your API that depends on this method, which makes the third approach easier.
Set the environment expliclitly during your tests. Example:
func TestFoo(t *testing.T) {
orig := os.Getenv("XYZ_URL")
os.Setenv("XYZ_URL", "http://example.com")
t.Cleanup(func() { os.Setenv("XYZ_URL", orig) })
/* do your tests here */
}
This approach does have limitations. In particular, it won't work to run multiple of these tests in parallel, so you still want to minimize the number of these tests you run.
This means that approaches 2 and 3 in conjunction with each other can be very powerful.
A final option is to create a function variable, that can be replaced in tests. I've talked about this in another post, but for your example it could look like this:
var getenv = os.Getenv
/* ... then in your code ... */
func foo() {
value := getenv("XYZ_URL") // Instead of calling os.Getenv directly
}
and in a test:
func TestFoo(t *testing.T) {
getenv = func(string) string { return "http://example.com/" }
/* ... your actual tests ... */
}
This has many of the same limitations as option #3, in that you cannot run multiple tests in parallel, as they will conflict.
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
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 :
GuestCartHandler interface , that allowed me to not call the actual implementation
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.
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
I have been unable to find a solution to mocking methods from golang packages.
For example, my project has code that attempts to recover when Os.Getwd() returns an error. The easiest way I can thinking of making a unit test for this, is by mocking the Os.Getwd() method to return an error, and verify that the code works accordingly.
I tried using testify, but it does not seem to be possible.
Anyone have any experience with that?
My own solution was to take the method as an argument, which allow to inject a "mock" instead when testing. Additionnaly, create an exported method as public facade and an unexported one for testing.
Example:
func Foo() int {
return foo(os.Getpid)
}
func foo(getpid func() int) int {
return getpid()
}
Looks like that taking a look at the os.Getwd test could give you some example of how you could test your code. Look for the functions TestChdirAndGetwd and TestProgWideChdir.
From reading those, it seems that the tests create temporary folders.
So a pragmatic approach would be to create temporary folders, like the tests mentioned above do, then break them so os.Getwd throws an error for you to catch on your test.
Just be careful doing these operations as they can mess up your system. I'd suggest testing in a lightweight container or a virtual machine.
I know this is a bit late but, here is how you can do it.
Testing DAL or SystemCalls or package calls is usually difficult. My approach to solve this problem is to push your system function calls behind an interface and then mock the functions of those interface. For example.
type SystemCalls interface {
Getwd() error
}
type SystemCallsImplementation struct{
}
func (SystemCallsImplementation) Getwd() error{
return Os.Getwd()
}
func MyFunc(sysCall SystemCalls) error{
sysCall.Getwd()
}
With this you inject your interface that has the system calls to your function. Now you can easily create a mock implementation of your interface for testing.
like
type MockSystemCallsImplementation struct{
err error
}
func (MockSystemCallsImplementation) Getwd() error{
return err //this can be set to nil or some value in your test function
}
Hope this answers your question.
This is the limitation of go compiler, google developers don't want to allow any hooks or monkey patching. If unit tests are important for you - than you have to select a method of source code poisoning. All these methods are the following:
You can't use global packages directly.
You have to create isolated version of method and test it.
Production version of method includes isolated version of method and global package.
But the best solution is to ignore go language completely (if possible).
Is there a way to execute test cases in GoLang in a pre-defined order.
P.S: I am writing test cases for life cycle of a event. So I have different api's for all the CURD operations. I want to run these test cases in a particular order as only if an event is created it can be destroyed.
Also can I get some value from one test case and pass it as input to another. (example:- To test the delete event api, I need a event_id which i get when I call create_event test case)
I am new to GoLang, can someone please guide me through.
Thanks in advance
The only way to do it is to encapsulate all your tests into one test function, that calls sub-functions in the right order and with the right context, and pass the testing.T pointer to each so they can fail. The down-side is that they will all appear as one test. But in fact that is the case - tests are stateless as far as the testing framework is concerned, and each function is a separate test case.
Note that although the tests may run in the order they are written in, I found no documentation stating that this is actually a contract of some sort. So even though you can write them in order and keep the state as external global variables - that's not recommended.
The only flexibility the framework gives you since go 1.4 is the TestMain method that lets you run before/after steps, or setup/teardown:
func TestMain(m *testing.M) {
if err := setUp(); err != nil {
panic(err)
}
rc := m.Run()
tearDown()
os.Exit(rc)
}
But that won't give you what you want. The only way to do that safely is to do something like:
// this is the whole stateful sequence of tests - to the testing framework it's just one case
func TestWrapper(t *testing.T) {
// let's say you pass context as some containing struct
ctx := new(context)
test1(t, ctx)
test2(t, ctx)
...
}
// this holds context between methods
type context struct {
eventId string
}
func test1(t *testing.T, c *context) {
// do your thing, and you can manipulate the context
c.eventId = "something"
}
func test2(t *testing.T, c *context) {
// do your thing, and you can manipulate the context
doSomethingWith(c.eventId)
}
Have a moduled application. Have a bunch of tests that use a set of application modules, each test requires different set. Some modules are tuned through the command-line, e.g:
func init() {
flag.StringVar(&this.customPath, "gamedir.custom", "", "Custom game resources directory")
}
But I cannot test this functionality. If I run
go test -test.v ./... -gamedir.custom=c:/resources
the runtime answers with
flag provided but not defined: -gamedir.custom
and fails the test.
What am I doing wrong with testing command-line args?
I think I got it what is wrong with flags in my case.
With the following command
go test -test.v ./... -gamedir.custom=c:/resources
the compiler runs one or several tests on a workspace. In my particular case there are several tests, because ./... means find and create test executable for every _test.go file found. The test executable applies all the additional params unless one or some of them is ignored within it.
Thus the test executables that do use param pass the test, all others fail. This may be overridden by running go test for each test.go separately, with appropriate set of params respectively.
You'll also get this message if you put your flag declarations inside of a test. Don't do this:
func TestThirdParty(t *testing.T) {
foo := flag.String("foo", "", "the foobar bang")
flag.Parse()
}
Instead use the init function:
var foo string
func init() {
flag.StringVar(&foo, "foo", "", "the foo bar bang")
flag.Parse()
}
func TestFoo() {
// use foo as you see fit...
}
The accepted answer, I found wasn't completely clear. In order to pass a parameter to a test (without an error) you must first consume that parameter using the flag. For the above example where gamedir.custom is a passed flag you must have this in your test file
var gamedir *string = flag.String("gamedir.custom", "", "Custom gamedir.")
Or add it to the TestMain
Note that from Go 1.13, you'll get the following error if you use flag.Parse() in init()
flag provided but not defined: -test.timeout
To fix this, you have to use TestMain
func TestMain(m *testing.M) {
flag.Parse()
os.Exit(m.Run())
}
TestFoo(t *testing.T) {}