8

Here is the sample code for the struct and its method

type A struct {}

func (a *A) perfom(string){
...
...
..
} 

Then i want to call the method from the function invoke() residing outside the package, sample code

var s := A{}
func invoke(url string){
   out := s.perfom(url)
   ...
   ...
} 

I want to write the test case for the function invoke by mocking the perform method of A.

In java, we have mockito, jmock framework to stub method calls.

Is there any way in go, we can mock the method call of the struct without introducing interfaces in source code ?

4
  • 2
    Don't mock it. (You cannot anyway.) Actually perform it. If performing the method would reach out: Mock that, e.g. a HTTP endpoint with net/http/httptest. Or have a global which can be turned to a fake during test (like a var Now func() time.Time = time.Now). Don't mock. This is bad practice. Commented Aug 9, 2018 at 7:09
  • I totally agree with you, but i need to mock some real grpc calls Commented Aug 9, 2018 at 9:02
  • 2
    Still: do not mock. Use a type B instead of A during test and let B provides a fake. Don't mock. Commented Aug 9, 2018 at 9:06
  • 7
    "Use a type B instead of A during test and let B provides a fake" - how is that different from mocking? "Mock objects are simulated objects that mimic the behavior of real objects in controlled ways." Commented Aug 9, 2018 at 12:40

2 Answers 2

8

To mock a method call, you need to make a mock of your structure.

With the code example you provided, I would recommend making a Performer interface that implements your Perform call. Both your real structure and your mock structure would implement this interface.

I would also recommend passing your structure as an argument to the invoke function instead of using a global variable.

Here is an example:

type Performer interface {
    perform()
}

type A struct {
}

func (a *A) perform() {
    fmt.Println("real method")
}

type AMock struct {
}

func (a *AMock) perform () {
    fmt.Println("mocked method")
}

func caller(p Performer) {
    p.perform()
}

In your tests, inject the mock to your invoke call. In your real code, inject the real structure to your invoke call.

Using a library like https://godoc.org/github.com/stretchr/testify/mock you will even be able to really easily verify that your method is called with the right arguments, called the right amount of times, and control the mock's behavior.

Sign up to request clarification or add additional context in comments.

3 Comments

wanted some way without using interface ,is it possible to achieve the behavior with out interface ?
Since you currently use a global, yeah. You just need to set performer := &realStruct{} above your invoke call, and when running your tests, set the global performer variable to &mockStruct{}. Other than that, keep the two structure definitions and their methods. Note however, that this is not very idiomatic. Using interfaces is the right solution, and this is a workaround.
See this playground example (note that you can't run this on playground, as it will work only if you use this method between multiple files.)
3

Assign the Perfom method to a global variable and re-assign it with a mocked one before running the test.

E.g.

51760447/a/a.go:

package a

type A struct{}

func (a *A) Perfom(url string) (string, error) {
    return "real data", nil
}

51760447/caller.go:

package caller

import "github.com/mrdulin/golang/src/stackoverflow/51760447/a"

var s = a.A{}
var (
    Perfom = s.Perfom
)

func Invoke(url string) string {
    out, _ := Perfom(url)
    return out
}

51760447/caller_test.go:

package caller_test

import (
    "reflect"
    "testing"

    caller "github.com/mrdulin/golang/src/stackoverflow/51760447"
)

func TestInvoke(t *testing.T) {
    oPerfom := caller.Perfom
    perfomCallCount := 0
    caller.Perfom = func(url string) (string, error) {
        perfomCallCount++
        return "fake data", nil
    }
    defer func() {
        caller.Perfom = oPerfom
    }()
    got := caller.Invoke("localhost")
    want := "fake data"
    if !reflect.DeepEqual(got, want) {
        t.Errorf("should return fake data, got:%v, want: %v", got, want)
    }
    if !reflect.DeepEqual(perfomCallCount, 1) {
        t.Errorf("Perfom method should be called once, got:%d", perfomCallCount)
    }
}

unit test result:

=== RUN   TestInvoke
--- PASS: TestInvoke (0.00s)
PASS
coverage: 100.0% of statements
ok      github.com/mrdulin/golang/src/stackoverflow/51760447    0.033s

coverage report:

enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.