So from what I've read, you can't test if a function is equal in Go, but I'm trying to solve a test-case issue, so any help in refactoring this would be helpful.
I have a constructor and I'm passing it some configuration values. Based on those configs, it assigns another constructor function to a member of the struct. Later, in a different method, it calls that new constructor. I did it this way because it made it easier to test the methods on the struct, since I could now create a test constructor and reassign the struct member to it, before calling the methods I was testing. Similar to the approach here: Mock functions in Go
Now though, I'm trying to write a test case on the struct constructor and I'm having a hard time figuring out how to test it.
Here's an example:
type requestBuilder func(portFlipArgs, PortFlipConfig) portFlipRequest
type portFlip struct {
config PortFlipConfig
args portFlipArgs
builder requestBuilder
}
func newPortFlip(args portFlipArgs, config PortFlipConfig) (*portFlip, error) {
p := &portFlip{args: args, config: config}
if p.netType() == "sdn" {
p.builder = newSDNRequest
} else if p.netType() == "legacy" {
p.builder = newLegacyRequest
} else {
return nil, fmt.Errorf("Invalid or nil netType: %s", p.netType())
}
return p, nil
}
The 'newSDNRequest' and 'newLegacyRequest' are the new constructors. I can't figure out how to test the newPortFlip method to make sure that's it assigning the right method to the 'builder' member, since you can't test function equality.
My only thought at this point is to have a 'builderType string' member, and just assign it to the name of the new constructor and then I could just test that. Something like:
func newPortFlip(args portFlipArgs, config PortFlipConfig) (*portFlip, error) {
p := &portFlip{args: args, config: config}
if p.netType() == "sdn" {
p.builderType = "newSDNRequest"
p.builder = newSDNRequest
} else if p.netType() == "legacy" {
p.builderType = "newLegacyRequest"
p.builder = newLegacyRequest
} else {
return nil, fmt.Errorf("Invalid or nil netType: %s", p.netType())
}
return p, nil
}
But that seemed rather frivolous, so I figured I should seek a better way before I did that.
Thoughts?
Make portFlip an interface and have newPortFlip construct either an sdnPortFlip or a legacyPortFlip depending on the incoming type. In your test you can then check it's returning the correct concrete type using a type assertion.
If you embed the common type into the SDN and legacy types then you can directly call those methods.
type portFlip interface {
build()
...
}
type portFlipCommon struct {
config PortFlipConfig
args portFlipArgs
}
type portFlipSdn struct {
portFlipCommon
}
type portFlipLegacy struct {
portFlipCommon
}
func (pf *portFlipCommon) netType() { ... }
func (pf *portFlipSdn) build() { ... }
func (pf *portFlipLegacy) build() { ... }
func newPortFlip(args portFlipArgs, config PortFlipConfig) (portFlip, error) {
var pf portFlip
p := &portFlipCommon{args: args, config: config}
if p.netType() == "sdn" {
// Either build directly or define build on the sdn type
pf = &portFlipSdn{*p}
} else if p.netType() == "legacy" {
// Either build directly or define build on the legacy type
pf = &portFlipLegacy{*p}
} else {
return nil, fmt.Errorf("Invalid or nil netType: %s", p.netType())
}
return pf, nil
}
Related
I am writing the following unit test:
type mockQux struct {
mock.Mock
MockInterface
calculateHash func(ctx context.Context, foo *entities.Foo, bar model.Bar, baz *model.Baz) (uint64, error)
}
type MockInterface interface {
On(methodName string, arguments ...interface{}) *mock.Call
AssertExpectations func(t mock.TestingT)
}
func (m *mockQux) TestCalculateHash(t *testing.T) {
mq := new(mockQux)
ctx := context.Background()
foo := &entities.Foo{
// foo's fields
}
// create instances of model.Bar and &model.Baz the same way I created foo variable
mq.On("calculateHash", ctx, foo, bar, baz).Return(uint64(123), nil)
hash, err := m.Mock.calculateHash(ctx, foo, bar, baz)
assert.Equal(t, uint64(123), hash)
assert.Nil(t, err)
m.Mock.AssertExpectations(t)
}
func TestCalculateHash(t *testing.T) {
m := new(mockQux)
m.TestCalculateHash(t)
}
Running go test and go test -c, why do I get a segfault on On and AssertExpectations?
I've tried debugging this with delve, but it crashes whenever I run it, even if I instruct it to step over the problematic lines. I guess the problem might be that I improperly reference the variables in arguments to mq.On or that I don't initialize all the fields (these structs are pretty complex).
There are too many fail point in your code sample to tell what's the exact root cause of the segfault. That's generally not how testify mock are used. You don't need either your MockInterface definition. Also you cannot have a segfault on 2 different locations, your program either segfault on On or AssertExpectations. That cannot be both. I strongly suspect though that your segfault come from this call:
hash, err := m.Mock.calculateHash(ctx, foo, bar, baz)
as this function pointer from your struct does not seem to be initialized.
About how to use mock in testify, we usually do something like that.
Here is a minimal nominal code without any business logic:
type ConcreteType struct {}
func (m *ConcreteType) DoSomething() int { return 3 }
type DoSomethinger interface {
DoSomething() int
}
func UseConcreteType(s DoSomethinger) error {
if val := s.DoSomething(); val != 3 {
return fmt.Errorf("unexpected value: %d", val)
}
}
In your test you would do something like that:
type DoSomethingMock struct {
mock.Mock
}
func (m *DoSomethingMock) DoSomething() int {
args := m.Called()
return args.Int(0)
}
func TestUseConcrete(t *testing.T) {
m := &DoSomethingMock{}
m.On("DoSomething").Return(4)
err := UseConcreteType(m)
require.Error(t, err)
}
I have a Client struct that wraps several clients (etcd and libvirt). Something like:
type Client struct {
etcd *clientv3
libvirt *libvirt.Connect
}
Once a client of my library wants to close its handle, I want to close both of these. So I have:
func (c *Client) Close() error {
c.etcd.Close()
c.libvirt.Close()
// Error handling excluded for brevity
}
What would be an elegant way to test this? My current best bet is to create two interfaces, one for each of the two wrapped clients. Those interfaces would include every single method from the two clients that my library uses. That should make it relatively easy to pass a mock of some sort instead of the real clients. This may be the way forward, but it feels awkward.
What are my other options?
As I mentioned in the comment, you can create a ClosableClient as shown below. Since each of your client will have Close method, you can do this. In your test file, you can create mock clients which have to only implement Close method. You don't need to make the interface implement all methods. In your code, you can use type assertion to convert the ClosableClient into specific client to access its functions. Here is a good example of type assertion.
I have added code snippet to show how you can use type assertion to get the underlying struct. Your mock clients in the test file need not implement Foo and Bar methods as the interface ClosableClient only needs Close method.
type ClosableClient interface {
Close()
}
type Etcd struct{}
func (e *Etcd) Close() {
fmt.Println("etcd closing")
}
func (e *Etcd) Foo() {
fmt.Println("etcd foo")
}
type Libvirt struct{}
func (l *Libvirt) Close() {
fmt.Println("libvirt closing")
}
func (l *Libvirt) Bar() {
fmt.Println("libvirt bar")
}
type Client struct {
etcd ClosableClient
libvirt ClosableClient
}
func (c *Client) Close() {
c.etcd.Close()
c.libvirt.Close()
}
func (c *Client) FooBar() {
etcd, ok := c.etcd.(*Etcd)
if !ok {
panic("etcd is of incorrect type")
}
etcd.Foo()
libvirt, ok := c.etcd.(*Libvirt)
if !ok {
panic("libvirt is of incorrect type")
}
libvirt.Bar()
}
Encouraged by poWar's comment that my idea was good, I went ahead like this:
I changed my Client struct to use interfaces for my libvirt and etcd connections:
type EtcdClient interface {
}
type LibvirtClient interface {
}
type Client struct {
etcd EtcdClient
libvirt LibvirtClient
}
When I tried to compile the package, I get an error message sort of like this:
./main.go:17:18: c.etcd.Close undefined (type EtcdClient is interface with no methods)
./main.go:21:24: c.libvirt.Close undefined (type LibvirtClient is interface with no methods)
Not surprising. I then added the simplest possible Close() method to the interfaces:
type EtcdClient interface {
Close()
}
type LibvirtClient interface {
Close()
}
Compiling once again gave me:
./main.go:56:10: cannot use etcd (type *clientv3.Client) as type EtcdClient in assignment:
*clientv3.Client does not implement EtcdClient (wrong type for Close method)
have Close() error
want Close()
./main.go:62:13: cannot use lv (type *libvirt.Connect) as type LibvirtClient in assignment:
*libvirt.Connect does not implement LibvirtClient (wrong type for Close method)
have Close() (int, error)
want Close()
And then I used that to fill in the interface definitions:
type EtcdClient interface {
Close() error
}
type LibvirtClient interface {
Close() (int, error)
}
Of course, Close was simple enough that I didn't have to go through this, but as I mentioned earlier, I'm calling many methods on these interfaces, and this way made it pretty simple to have the compiler help me fill in the interface definitions.
For the tests, I could then make fakes (mocks? stubs? I always forget the difference). Here is the test file in its entirety:
package main
import (
"errors"
"testing"
)
type FakeEtcdClient struct {
wasClosed bool
failToClose bool
}
func (f *FakeEtcdClient) Close() error {
if f.failToClose {
return errors.New("Fake Etcd failed to Close")
}
f.wasClosed = true
return nil
}
type FakeLibvirtClient struct {
wasClosed bool
failToClose bool
}
func (f *FakeLibvirtClient) Close() (int, error) {
if f.failToClose {
return 0, errors.New("Fake libvirt failed to Close")
}
f.wasClosed = true
return 0, nil
}
func TestClient_Close(t *testing.T) {
type fields struct {
etcd EtcdClient
libvirt LibvirtClient
}
tests := []struct {
name string
fields fields
wantErr bool
}{
{"Happy path", fields{&FakeEtcdClient{}, &FakeLibvirtClient{}}, false},
{"Etcd fails", fields{&FakeEtcdClient{failToClose: true}, &FakeLibvirtClient{}}, true},
{"Libvirt fails", fields{&FakeEtcdClient{}, &FakeLibvirtClient{failToClose: true}}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Client{
etcd: tt.fields.etcd,
libvirt: tt.fields.libvirt,
}
if err := c.Close(); (err != nil) != tt.wantErr {
t.Errorf("Client.Close() error = %v, wantErr %v", err, tt.wantErr)
} else {
if !tt.wantErr {
// We only check if the clients have been closed if
// Client.Close() returns successfully.
if !c.etcd.(*FakeEtcdClient).wasClosed {
t.Error("Etcd connection was not closed")
}
if !c.libvirt.(*FakeLibvirtClient).wasClosed {
t.Error("Libvirt connection was not closed")
}
}
}
})
}
}
I have a singleton in my application, but it's published not as a struct directly, but as an interface (because I want to be able to dynamically select the particular implementation upon singleton initialization). Here's the code:
var once sync.Once
var instance defaultConfiguration
type Configuration interface {
GetFoo() string
}
type defaultConfiguration struct {
}
func (dc defaultConfiguration) GetFoo() string {
return "foo"
}
func NewConfiguration() Configuration {
once.Do(func() {
instance = defaultConfiguration{}
})
return instance
}
Then I decided to write a unit-test that would check that NewConfiguration() will actually return the same instance each time:
func TestNewConfigurationSameInstance(t *testing.T) {
configuration1 := NewConfiguration()
configuration2 := NewConfiguration()
if &configuration1 != &configuration2 {
t.Error()
}
}
I thought it would make sense to compare the addresses of the returned instances, however, this test fails.
Then I thought, well, maybe I have to return a pointer to an instance, so I've changed the code to look like this:
func NewConfiguration() *Configuration {
once.Do(func() {
instance = defaultConfiguration{}
})
return &instance
}
But this doesn't even compile: it fails with the error message
cannot use &instance (type *defaultConfiguration) as type *Configuration in return argument:
*Configuration is pointer to interface, not interface
And I've got very confused. Why can't I return a pointer to an interface? Or, why returning defaultConfiguration as Configuration is valid, but returning *defaultConfiguration as *Configuration is not?
And, after all, what is the proper unit-test for my use-case?
Your code should be:
var once sync.Once
var instance *defaultConfiguration
type Configuration interface {
GetFoo() string
}
type defaultConfiguration struct {
}
func (dc *defaultConfiguration) GetFoo() string {
return "foo"
}
func NewConfiguration() Configuration {
once.Do(func() {
instance = &defaultConfiguration{}
})
return instance
}
Since Configuration is an interface and you want a pointer to defaultConfiguration to implement it.
Pointers to interfaces (e.g. *Configuration) are rarely needed. An interface is already a reference value, and it's perfectly fine for a pointer to some type to implement an interface.
For more background on the root issue read this answer or similar resources.
I'm working with Golang, and currently I'm doing some fun unit test with Testify, my file look like this
type myStruct struct {
field_1 string
}
func (self *myStruct) writeFirst() {
//doing something
//modify field_1
self.writeSecond()
}
func (self *myStruct) writeSecond() {
//doing something
}
In this case I'm testing writeFirst() but I'm trying to replace writeSecond() because it is using http stuff that I don't want to use because it access to internet.
I think that use a second struct and set myStruct as anonymous field will be the solution, but it's not working because me second struct and myStruct have a diferent context.
In this case I can't use mocks cause writeSecond is a method of the struct.
My test case looks like this:
func TestWriteFirst(t *testing.T) {
myStc := myStruct{}
assert.Equal(t,"My response", myStc.field_1)
}
All that I want is testing writeFirst without pass to writeSecond()
To illustrate the kind of refactoring mentioned by Not-a-Golfer in the comments, you could consider calling your second function only on an instance that is an interface:
type F2er interface {
Func2()
}
type S struct{ _f2 F2er }
var s = &S{}
func (s *S) f2() F2er {
if s._f2 == nil {
return s
}
return s._f2
}
func (s *S) Func1() {
fmt.Println("s.Func1")
s.f2().Func2()
}
Here: Func1 calls Func2 on s.f2(), not directly s.
If nothing has been set in s, s.f2() returns... itself: s
if s._f2 was replaced by any other struct which implements Func2, s.f2() returns that instance instead of itself.
See a complete example in this playground script.
Output:
TestFunc1
s.Func1
s.Func2
TestFunc1bis
s.Func1
testS.Func2 <=== different Func2 call
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.