I am having trouble writing unit tests in Go for a rather common use-case/pattern.
Imagine, if you will, something like this:
package main
type Resource struct {
name string
}
type ResourceManager interface {
GetResource(id string) (*Resource, error)
GetAllResources() ([]*Resource, error)
}
type ResourceManagerImpl struct {
}
func (r *ResourceManagerImpl) GetResource(id string) (*Resource, error) {
resource := &Resource{}
var err error
// fetch resource.
// ...
return resource, err
}
func (r *ResourceManagerImpl) GetAllResources() ([]*Resource, error) {
var resources []*Resource
var err error
// for _, id := range ids {
// resource = r.GetResource(id)
// resources = append(resources, resource)
// }
return resources, err
}
It's a common pattern to have the GetAllResources invoke GetResource repeatedly as-needed.
I can use gomock or testify to test all permutations of GetResource. But, when testing GetAllResource, I'd like to mock GetResource. Otherwise, testing would become sort-of-a nightmare. This is how one would do it in easymock or mockito in case of Java using partial mocks. But, it's not clear how the same could be achieved in Golang.
Specifically, I couldn't find how to partially mock the struct. Most suggestions revolve around breaking such structs but in this case, the struct is already at its bare minimum. It seems like a fair ask to not break the ResourceManager interface (into single and multi) for the sake of testing as that would not make much sense and would be kludgy at best and wouldn't scale well as more such methods make into the interface.
This is how I deal with this kind of situation:
func (r *ResourceManagerImpl) GetAllResources() ([]*Resource, error) {
return getAllResources(r)
}
func getAllResources(r ResourceManager) ([]*Resource,error) {
...
}
Then you test getAllResources instead of GetAllResources with a mocked r. If you have a situation where GetAllResources is called from the code and you have to mock GetAllResources, you can do:
var getAllResources=func(r ResourceManager) ([]*Resource,error) {
...
}
And assign getAllResources to a test instance.
Related
I am new to golang and came from a java background.
Here is my problem today: How to unit test a component that uses a third-party library that doesn't provide an interface in Golang? Here is my concrete example:
I have a class that uses golang mongodb driver to implement some DB operations like below:
package mypackage
type myClientBeingTested struct {
client *mongo.Client
}
func (mc *myClientBeingTested) FindOne(filter interface{}) (*mongo.SingleResult, error) {
result := mc.client.FindOne(context.Background(), filter)
if result.Err() == mongo.ErrNoDocuments {
return nil, nil
} else {
return nil, Errors.New("My own error message")
}
return result, nil
}
Now I'd like to write some unit tests for this method and realized that it's impossible to mock a third party dependency that doesn't have an interface implementation. In the example above, mongo.Client is a struct type. After some researching and thinking, the only possible way seems to be like below:
package mypackage
type myClientBeingTested struct {
client *mongo.Client
}
var findOneFunc = func(client *mongo.Client, ctx context.Context, filter interface{}) (*mongo.SingleResult, error) {
return client.findOne(ctx, filter)
}
func (mc *myClientBeingTested) FindOne(filter interface{}) (*mongo.SingleResult, error) {
result := findOneFunc(mc.client, filter)
if result.Err() == mongo.ErrNoDocuments {
return nil, nil
} else {
return nil, Errors.New("My own error message")
}
return result, nil
}
Then in my unit test I can stub findOneFunc with my own stub like below
findOneFunc = func(client *mongo.Client, ctx context.Context, filter interface{}) (*mongo.SingleResult, error) {
// my own implementation
}
But this seems to be a hack. Is there any authentic/recommended way to handling situations like that? Appreciate your responses!
It should be possible to write your own interface for the methods that you need to use from a struct imported from a 3rd party library.
type MongoClient interface {
FindOne(context.Context, mongo.D) (*mongo.SingleResult, error)
}
type myClientBeingTested struct {
client MongoClient
}
// in tests
type mockMongoClient struct {
// implement MongoClient, pass in to myClientBeingTested
}
However for most apps it provides a better guarantee to run tests against a local or in memory database to verify that everything works end to end. If that becomes too slow it can make sense to mock at the business logic level instead of the database query level.
For example:
type User struct {}
type UserMgmt interface {
Login(email, pass string) (*User, error)
}
// for testing api or workflows
type MockUserMgmt struct {}
// for production app
type LiveUserMgmt struct {
client *mongo.Client
}
In the unit test it would look like:
// user_mgmt_test.go test code
userMgmt := &LiveUserMgmt{client: mongo.Connect("localhost")}
// test public library methods
In api or workflow tests it would look like:
userMgmt := &MockUserMgmt{}
// example pass to api routes
api := &RequestHandler{
UserMgmt: userMgmt,
}
EDIT:
I'm too new to comment on my post, but re your question about mocking the struct, you apply the same principle. If the mongo type is a struct, you can create an interface (even with the same name) and depend on the interface instead of directly depending on the struct. Then via the interface you can mock out the methods you need to.
// The mongo struct you depend on and need to mock
type mongo struct {
someState string
}
// The real world function you need to mock out
func (m *mongo) Foo() error {
// do stuff
return nil
}
// Construct an interface with a method that matches the signature you need to mock
type mockableMongoInterface interface {
Foo() error
}
Now depend on mockableMongoInterface instead of directly on mongo. You can still pass your third party mongo struct to sites where you need it, because go will understand the type via the interface.
This aligns with Adrian's comment on your question.
Let's say I have a package with the following code:
package paths
type FilePath struct {
PathA string
}
func (c FilePath) GetPathA() string {
if err := PathExists(PathA); err != nil {
return ""
}
return PathA + "foo"
}
func PathExists(p string) error {
// call os and file methods
return err
}
How do I mock out the PathExists dependency to test FilePath? Also, method PathExists is being used by a lot of other packages as well. (I am open to suggestions of refactoring this to make it test friendly, keeping the following pointers in mind)
I have come across a few different approaches but none of them seems intuitive or idiomatic to me.
Have a global variable PE := PathExists in the package; in GetPathA, call err := PE(PathA) and in the test overwrite PE with a mock method.
Issue: If test package is something like paths_test, I will have to export PE which allows clients of the package to overwrite it as well.
Make PathExists a field of FilePath and mock the field in test.
Issue: Clients when using the package, will have to initialize PathExists field, or I provide a constructor of the form NewFilePath(PathtA string) which initializes the fields for me. In the actual use case there are a lot of fields, hence this approach fails as well.
Use an interface and embed it within the struct. When client uses it initialize with the actual method and for test mock it.
type PathExistser interface{
PathExists(p string) error
}
type FilePath struct{
PathA string
PathExister
}
type Actual struct{}
func (a Actual) PathExists(p string) error {
return PathExists(p)
}
Issue: Client again needs to provide the right implementation of the interface.
I have learnt of few more approaches doing something simimlar to the above options, such as make the method PathExists an argument for GetPathA, etc. All have the same concerns. Basically, I don't want the users of this package to have to figure out what should be the right input parameter to make sure the struct works as expected. Neither do I want the users to overwrite the behaviour PathExists.
This seems like a very straightforward problem and I seem to be missing something very funamental about go testing or mocking. Any help would be appreciated, thanks.
Method names are just for example. In reality GetPathA or PathExists would be way more complex.
To address the issue from your 1. approach, you can use an internal package which you'll then be able to import in paths_test but clients of your package won't be.
package paths
import (
// ...
"<your_module_path>/internal/osutil"
)
func PathExists(p string) error {
return osutil.PathExists(p)
}
package osutil
var PathExists = func(p string) error {
// call os and file methods
return err
}
// Use a mutex to make sure that if you have
// multiple tests using mockPathExists and running
// in parallel you avoid the possiblity of a data race.
//
// NOTE that the mutex is only useful if *all* of your tests
// use MockPathExists. If only some do while others don't but
// still directly or indirectly cause the paths.PathExists
// function to be invoked then you still can run into a data
// race problem.
var mu sync.Mutex
func MockPathExists(mock func(p string) error) (unmock func()) {
mu.Lock()
original := PathExists
PathExists = mock
return func() {
PathExists = original
mu.Unlock()
}
}
package paths_test
import (
// ...
"<your_module_path>/internal/osutil"
)
func TestPathExists(t *testing.T) {
unmock := osutil.MockPathExists(myPathExistsMockImpl)
defer unmock()
// do your test
}
I'm using github.com/aws/aws-sdk-go/aws/request to get presigned URLs, which I need to upload files to s3 bucket in AWS. I'm writing the test currently, for that I need to mock func (r *Request) Presign(expire time.Duration). request.Request is a struct, not an interface, so i have no idea, how can I mock it.
This isn't directly answering your question, but it might remove the basis of the question altogether.
A neat thing in Go, is that you are able to easily isolate your dependencies using interfaces. If your code, the part that you need to test, is using Presign indirectly, it is trivial to test.
I.e. create an interface
type HigherLevelAws interface {
Upload(file string) error
}
and use this interface in your code, along with Upload. Then you can easily mock this using e.g. https://godoc.org/github.com/stretchr/testify/mock
The actual implementation, would look something like this
type ActualAwsImpl struct {
aws *aws.Client
}
func (a *ActualAwsImpl) Upload(file string) error {
aws.Presign...
}
This allows you to test the business part of your code, but of course, still leaves untested code in ActualAwsImpl. This untested code, however, may be guaranteed to work by virtue of unit and integration tests in the aws sdk itself.
Either way, in my organization, we test this using fake aws services run in docker (e.g. https://github.com/gliffy/fake-s3).
I had to Mock a Request for a S3 command. Code below:
req, result := r.s3.ListObjectsV2Request(&s3.ListObjectsV2Input{
Bucket: aws.String(configs.Settings.S3Bucket),
})
err := req.Send()
The Mock function had to return a Request Object
func (m *mockS3ProviderClient) ListObjectsV2Request(input *s3.ListObjectsV2Input) (req *request.Request, output *s3.ListObjectsV2Output) {
err := input.Validate()
req = &request.Request{
HTTPRequest: &http.Request{},
Error: err,
}
output = &s3.ListObjectsV2Output{
Contents: []*s3.Object{
{Key:aws.String("File1")},
{Key:aws.String("File2")},
},
}
return req, output
}
You can create an interface for the function directly, like this:
type presigner interface {
Presign(expire time.Duration) (string, error)
}
If you implement your logic in a separate function that takes a presigner as a parameter like this (called dependency injection):
func Upload(p presigner, files string) error {
// ...
res, err := p.Presign(someduration)
if err != nil {
return err
}
// and so on
}
Then it's easy to mock in your tests - just implement the presigner interface and have the Presign function return what you expect:
type presignerMock struct {}
func (p *presignerMock) Presign(d time.Duration) (string, error) {
return "yay", nil
}
To test different scenarios you can add fields to the presignerMock and return them in your implementation:
type presignerMock {
res string
err error
}
func (p *presignerMock) Presign(d time.Duration) (string, error) {
return p.res, p.err
}
How do I test error conditions, & other unexpected code flow, in Golang?
Suppose I have code as follows:
import crypto
func A(args) error {
x, err := crypto.B()
if err != nil {
return err
}
return nil
}
B is some function. I believe that the only way for me to test this failure scenario is to change the value of B for my test (mock it so it returns an error). Things I have tried:
1) monkeypatch the function just before the test and unpatch afterwards.
This was a horrible idea. Caused all sorts of weird issues as the
tests were running.
2) Pass B as an argument to A. This is all well and good but it also
means that I have to change the definition for A and then update
every use of it every time the implementation changes. Furthermore,
A may be using many imported functions and putting them all in the
type definition for A just seems ugly and not idiomatic.
3) Change the value of B for my test, then change it back.
import crypto.B
cryptoB = crypto.B
func A(args) error {
x, err := cryptoB()
if err != nil {
return err
}
...
}
func Test_A(t *testing.T) {
oldB := cryptoB
cryptoB = mockB
// run test
cryptoB = oldB
...
}
I've been using method #3 as it allows me fine grained unit testing control while not creating too much overhead. That said, it's ugly and causes confusion e.g. "Why are we renaming all of the imported functions?".
What should I be doing? Please help me so my codes can be more better :)
Like you, I've never seen a solution to this problem that's totally satisfied me.
In regards to your example 3, remember that you can defer the reset of the cryptoB. This, combined with good naming of the mock functions, would make it clear what you are trying to accomplish. There are obviously still code-style issues with this approach, with having all of your references listed line by line, twice, at the start of your file.
func TestSomething(t *testing.T) {
cryptoB = mockedFunc
defer func() {
cryptoB = crypto.B
}
// Testing goes on here
}
Option 4
The other approach (which I would favor) would be to turn the functions you export into methods of a CryptoA struct. The struct would store whatever dependencies and state it requires. Something like this:
type CryptoA struct {
cryptoB func() error
}
func (a *CryptoA) CryptoA() error {
return a.cryptoB()
}
func NewCryptoA() *CryptoA {
return &CryptoA{
cryptoB: func() error {
return nil
},
}
}
and mocking would be very similar:
func TestSomething(t *testing.T) {
a := NewCryptoA()
a.cryptoB = mockedFunc
// Testing goes on here
}
With this approach you lose some by your API having an extra step for invocation, a := NewCryptoA(), and you still have to name all of your dependencies, but you make gains by having the state of your API specific to each client.
Maybe there is a flaw in your API, and you leak data somewhere unexpected, or there is some state modifications that you don't expect. If you create a new CryptoA for each caller, then maybe the amount of data you leak, or the number of clients with a corrupted state, is limited, and therefore the impact less severe/abusable. I'm obviously spitballing at how this applies to your codebase, but hopefully you can get the idea of how this is a win.
Also, if you want to give the ability for users to specify their own hash algorithm, you can swap it out internally, and since it's private you maintain confidence that the function is up to the standards of your API. Again, I'm obviously spitballing.
I'll be skimming the answers to see if there's an idiomatic way to do this that I'm unaware of.
My preferred approach is usually to make A a method, and store the dependencies in the receiver object. Example:
import crypto;
type CryptoIface interface {
B() (string, error)
}
type standardCrypto struct {}
var _ CryptoIface = &standardCrypto{}
func (c *standardCrypto) B() (string, error) {
return crypto.B()
}
func main() {
crypto = &standardCrypto{}
err = A(crypto, ...)
// stuff
}
func A(crypto CryptoIface, args ...string) error {
result, err := crypto.B()
if err != nil {
return err
}
// do something with result
return nil
}
Then for your tests, you can easily create a mock version of the CryptoIface:
type mockCrypto struct {
Bfunc func(args ...string) error
}
func (c *mockCrypto) B(args ...string) error {
return c.Bfunc(args...)
}
func TestA(t *testing.T) {
c := &mockCrypto{
Bfunc: func(_ ...string) error {
return errors.New("test error")
}
}
err := A(c)
if err.String() != "test error" {
t.Errorf("Unexpected error: %s", err)
}
}
This approach is usually most useful for large projects or APIs where it makes sense to include a number of methods on a single object. Use your discretion. For smaller cases, this will be overkill.
I'm puzzled with dependencies. I want to be able to replace some function calls with mock ones. Here's a snippet of my code:
func get_page(url string) string {
get_dl_slot(url)
defer free_dl_slot(url)
resp, err := http.Get(url)
if err != nil { return "" }
defer resp.Body.Close()
contents, err := ioutil.ReadAll(resp.Body)
if err != nil { return "" }
return string(contents)
}
func downloader() {
dl_slots = make(chan bool, DL_SLOT_AMOUNT) // Init the download slot semaphore
content := get_page(BASE_URL)
links_regexp := regexp.MustCompile(LIST_LINK_REGEXP)
matches := links_regexp.FindAllStringSubmatch(content, -1)
for _, match := range matches{
go serie_dl(match[1], match[2])
}
}
I'd like to be able to test downloader() without actually getting a page through http - i.e. by mocking either get_page (easier since it returns just the page content as a string) or http.Get().
I found this thread which seems to be about a similar problem. Julian Phillips presents his library, Withmock as a solution, but I'm unable to get it to work. Here's the relevant parts of my testing code, which is largely cargo cult code to me, to be honest:
import (
"testing"
"net/http" // mock
"code.google.com/p/gomock"
)
...
func TestDownloader (t *testing.T) {
ctrl := gomock.NewController()
defer ctrl.Finish()
http.MOCK().SetController(ctrl)
http.EXPECT().Get(BASE_URL)
downloader()
// The rest to be written
}
The test output is following:
ERROR: Failed to install '_et/http': exit status 1 output: can't load
package: package _et/http: found packages http (chunked.go) and main
(main_mock.go) in
/var/folders/z9/ql_yn5h550s6shtb9c5sggj40000gn/T/withmock570825607/path/src/_et/http
Is the Withmock a solution to my testing problem? What should I do to get it to work?
Personally, I don't use gomock (or any mocking framework for that matter; mocking in Go is very easy without it). I would either pass a dependency to the downloader() function as a parameter, or I would make downloader() a method on a type, and the type can hold the get_page dependency:
Method 1: Pass get_page() as a parameter of downloader()
type PageGetter func(url string) string
func downloader(pageGetterFunc PageGetter) {
// ...
content := pageGetterFunc(BASE_URL)
// ...
}
Main:
func get_page(url string) string { /* ... */ }
func main() {
downloader(get_page)
}
Test:
func mock_get_page(url string) string {
// mock your 'get_page()' function here
}
func TestDownloader(t *testing.T) {
downloader(mock_get_page)
}
Method2: Make download() a method of a type Downloader:
If you don't want to pass the dependency as a parameter, you could also make get_page() a member of a type, and make download() a method of that type, which can then use get_page:
type PageGetter func(url string) string
type Downloader struct {
get_page PageGetter
}
func NewDownloader(pg PageGetter) *Downloader {
return &Downloader{get_page: pg}
}
func (d *Downloader) download() {
//...
content := d.get_page(BASE_URL)
//...
}
Main:
func get_page(url string) string { /* ... */ }
func main() {
d := NewDownloader(get_page)
d.download()
}
Test:
func mock_get_page(url string) string {
// mock your 'get_page()' function here
}
func TestDownloader() {
d := NewDownloader(mock_get_page)
d.download()
}
If you change your function definition to use a variable instead:
var get_page = func(url string) string {
...
}
You can override it in your tests:
func TestDownloader(t *testing.T) {
get_page = func(url string) string {
if url != "expected" {
t.Fatal("good message")
}
return "something"
}
downloader()
}
Careful though, your other tests might fail if they test the functionality of the function you override!
The Go authors use this pattern in the Go standard library to insert test hooks into code to make things easier to test:
https://golang.org/src/net/hook.go
https://golang.org/src/net/dial.go#L248
https://golang.org/src/net/dial_test.go#L701
I'm using a slightly different approach where public struct methods implement interfaces but their logic is limited to just wrapping private (unexported) functions which take those interfaces as parameters. This gives you the granularity you would need to mock virtually any dependency and yet have a clean API to use from outside your test suite.
To understand this it is imperative to understand that you have access to the unexported methods in your test case (i.e. from within your _test.go files) so you test those instead of testing the exported ones which have no logic inside beside wrapping.
To summarize: test the unexported functions instead of testing the exported ones!
Let's make an example. Say that we have a Slack API struct which has two methods:
the SendMessage method which sends an HTTP request to a Slack webhook
the SendDataSynchronously method which given a slice of strings iterates over them and calls SendMessage for every iteration
So in order to test SendDataSynchronously without making an HTTP request each time we would have to mock SendMessage, right?
package main
import (
"fmt"
)
// URI interface
type URI interface {
GetURL() string
}
// MessageSender interface
type MessageSender interface {
SendMessage(message string) error
}
// This one is the "object" that our users will call to use this package functionalities
type API struct {
baseURL string
endpoint string
}
// Here we make API implement implicitly the URI interface
func (api *API) GetURL() string {
return api.baseURL + api.endpoint
}
// Here we make API implement implicitly the MessageSender interface
// Again we're just WRAPPING the sendMessage function here, nothing fancy
func (api *API) SendMessage(message string) error {
return sendMessage(api, message)
}
// We want to test this method but it calls SendMessage which makes a real HTTP request!
// Again we're just WRAPPING the sendDataSynchronously function here, nothing fancy
func (api *API) SendDataSynchronously(data []string) error {
return sendDataSynchronously(api, data)
}
// this would make a real HTTP request
func sendMessage(uri URI, message string) error {
fmt.Println("This function won't get called because we will mock it")
return nil
}
// this is the function we want to test :)
func sendDataSynchronously(sender MessageSender, data []string) error {
for _, text := range data {
err := sender.SendMessage(text)
if err != nil {
return err
}
}
return nil
}
// TEST CASE BELOW
// Here's our mock which just contains some variables that will be filled for running assertions on them later on
type mockedSender struct {
err error
messages []string
}
// We make our mock implement the MessageSender interface so we can test sendDataSynchronously
func (sender *mockedSender) SendMessage(message string) error {
// let's store all received messages for later assertions
sender.messages = append(sender.messages, message)
return sender.err // return error for later assertions
}
func TestSendsAllMessagesSynchronously() {
mockedMessages := make([]string, 0)
sender := mockedSender{nil, mockedMessages}
messagesToSend := []string{"one", "two", "three"}
err := sendDataSynchronously(&sender, messagesToSend)
if err == nil {
fmt.Println("All good here we expect the error to be nil:", err)
}
expectedMessages := fmt.Sprintf("%v", messagesToSend)
actualMessages := fmt.Sprintf("%v", sender.messages)
if expectedMessages == actualMessages {
fmt.Println("Actual messages are as expected:", actualMessages)
}
}
func main() {
TestSendsAllMessagesSynchronously()
}
What I like about this approach is that by looking at the unexported methods you can clearly see what the dependencies are. At the same time the API that you export is a lot cleaner and with less parameters to pass along since the true dependency here is just the parent receiver which is implementing all those interfaces itself. Yet every function is potentially depending only on one part of it (one, maybe two interfaces) which makes refactors a lot easier. It's nice to see how your code is really coupled just by looking at the functions signatures, I think it makes a powerful tool against smelling code.
To make things easy I put everything into one file to allow you to run the code in the playground here but I suggest you also check out the full example on GitHub, here is the slack.go file and here the slack_test.go.
And here the whole thing.
I would do something like,
Main
var getPage = get_page
func get_page (...
func downloader() {
dl_slots = make(chan bool, DL_SLOT_AMOUNT) // Init the download slot semaphore
content := getPage(BASE_URL)
links_regexp := regexp.MustCompile(LIST_LINK_REGEXP)
matches := links_regexp.FindAllStringSubmatch(content, -1)
for _, match := range matches{
go serie_dl(match[1], match[2])
}
}
Test
func TestDownloader (t *testing.T) {
origGetPage := getPage
getPage = mock_get_page
defer func() {getPage = origGatePage}()
// The rest to be written
}
// define mock_get_page and rest of the codes
func mock_get_page (....
And I would avoid _ in golang. Better use camelCase
the simplest way is to set function into a global variable and before test set your custom method
// package base36
func GenerateRandomString(length int) string {
// your real code
}
// package teamManager
var RandomStringGenerator = base36.GenerateRandomString
func (m *TeamManagerService) CreateTeam(ctx context.Context) {
// we are using the global variable
code = RandomStringGenerator(5)
// your application logic
return nil
}
and in your test, you must first mock that global variable
teamManager.RandomStringGenerator = func(length int) string {
return "some string"
}
service := &teamManager.TeamManagerService{}
service.CreateTeam(context.Background())
// now when we call any method that user teamManager.RandomStringGenerator, it will call our mocked method
another way is to pass RandomStringGenerator as a dependency and store it inside TeamManagerService and use it like this:
// package teamManager
type TeamManagerService struct {
RandomStringGenerator func(length int) string
}
// in this way you don't need to change your main/where this code is used
func NewTeamManagerService() *TeamManagerService {
return &TeamManagerService{RandomStringGenerator: base36.GenerateRandomString}
}
func (m *TeamManagerService) CreateTeam(ctx context.Context) {
// we are using the struct field variable
code = m.RandomStringGenerator(5)
// your application logic
return nil
}
and in your test, you can use your own custom function
myGenerator = func(length int) string {
return "some string"
}
service := &teamManager.TeamManagerService{RandomStringGenerator: myGenerator}
service.CreateTeam(context.Background())
you are using testify like me :D you can do this
// this is the mock version of the base36 file
package base36_mock
import "github.com/stretchr/testify/mock"
var Mock = mock.Mock{}
func GenerateRandomString(length int) string {
args := Mock.Called(length)
return args.String(0)
}
and in your test, you can use your own custom function
base36_mock.Mock.On("GenerateRandomString", 5).Return("my expmle code for this test").Once()
service := &teamManager.TeamManagerService{RandomStringGenerator: base36_mock.GenerateRandomString}
service.CreateTeam(context.Background())
Warning: This might inflate executable file size a little bit and cost a little runtime performance. IMO, this would be better if golang has such feature like macro or function decorator.
If you want to mock functions without changing its API, the easiest way is to change the implementation a little bit:
func getPage(url string) string {
if GetPageMock != nil {
return GetPageMock()
}
// getPage real implementation goes here!
}
func downloader() {
if GetPageMock != nil {
return GetPageMock()
}
// getPage real implementation goes here!
}
var GetPageMock func(url string) string = nil
var DownloaderMock func() = nil
This way we can actually mock one function out of the others. For more convenient we can provide such mocking boilerplate:
// download.go
func getPage(url string) string {
if m.GetPageMock != nil {
return m.GetPageMock()
}
// getPage real implementation goes here!
}
func downloader() {
if m.GetPageMock != nil {
return m.GetPageMock()
}
// getPage real implementation goes here!
}
type MockHandler struct {
GetPage func(url string) string
Downloader func()
}
var m *MockHandler = new(MockHandler)
func Mock(handler *MockHandler) {
m = handler
}
In test file:
// download_test.go
func GetPageMock(url string) string {
// ...
}
func TestDownloader(t *testing.T) {
Mock(&MockHandler{
GetPage: GetPageMock,
})
// Test implementation goes here!
Mock(new(MockHandler)) // Reset mocked functions
}
I have been in similar spot. I was trying to write unitTest for a function which had numerous clients calling it. let me propose 2 options that I explored. one of which is already discussed in this thread, I will regardless repeat it for the sake of people searching.
Method 1: Declaring function you wanna mock as a Global variable
one option is declaring a global variable (has some pit falls).
eg:
package abc
var getFunction func(s string) (string, error) := http.Get
func get_page(url string) string {
....
resp, err := getFunction(url)
....
}
func downloader() {
.....
}
and the test func will be as follows:
package abc
func testFunction(t *testing.T) {
actualFunction := getFunction
getFunction := func(s string) (string, error) {
//mock implementation
}
defer getFunction = actualFunction
.....
//your test
......
}
NOTE: test and actual implementation are in the same package.
there are some restrictions with above method thought!
running parallel tests is not possible due to risk of race conditions.
by making function a variable, we are inducing a small risk of reference getting modified by future developers working in same package.
Method 2: Creating a wrapped function
another method 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:
package abc
type getOperation func(s string) (string, error)
func get_page(url string, op getOperation) string {
....
resp, err := op(url)
....
}
//contains only 2 lines of code
func downloader(get httpGet) {
op := http.Get
content := wrappedDownloader(get, op)
}
//wraps all the logic that was initially in downloader()
func wrappedDownloader(get httpGet, op getOperation) {
....
content := get_page(BASE_URL, op)
....
}
now for testing the actual logic, you will test calls to wrappedDownloader instead of Downloader and you would pass it a mocked getOperation. this is allow you to test all the business logic while not violating your API contract with current clients of the method.
Considering unit test is the domain of this question, highly recommend you to use monkey. This Package make you to mock test without changing your original source code. Compare to other answer, it's more "non-intrusive".
main
type AA struct {
//...
}
func (a *AA) OriginalFunc() {
//...
}
mock test
var a *AA
func NewFunc(a *AA) {
//...
}
monkey.PatchMethod(reflect.TypeOf(a), "OriginalFunc", NewFunc)
Bad side is:
Reminded by Dave.C, This method is unsafe. So don't use it outside of unit test.
Is non-idiomatic Go.
Good side is:
Is non-intrusive. Make you do things without changing the main code. Like Thomas said.
Make you change behavior of package (maybe provided by third party) with least code.