How to mock for same input and different return values in a for loop in golang

10,783

Solution 1

I had a similar problem.

The solution was the method Once()

In your mock add an .Once() and repeat the mock with each result you need.

Something like this:

lib.Mock.On("method", arg).Return(test.mockError).Once()
lib.Mock.On("method", arg).Return(nil).Once()

Each mock result will be returned only once.

https://godoc.org/github.com/stretchr/testify/mock#Call.Once

Solution 2

The answer @Marcos provided works well when the result needs to be returned exactly once.
But in the scenario where each return value needs to be returned multiple (unknown) times, it won't work.

The way I solved it is by manipulating the mock.ExpectedCalls directly. In my case the mock was holding only a single method, so it was simple to just cleanup the whole ExpectedCalls slice, but in case there are multiple methods, the ExpectedCalls slice can be iterated, and update only the required call.

here is a working example for the simple case:

lib.Mock.On("method", arg).Return("1")

assert.Equal(t, lib.Mock.method(arg), "1")
assert.Equal(t, lib.Mock.method(arg), "1")
....
assert.Equal(t, lib.Mock.method(arg), "1")

lib.Mock.ExpectedCalls = nil // cleanup the previous return value
lib.Mock.On("method", arg).Return("2")
assert.Equal(t, lib.Mock.method(arg), "2")
assert.Equal(t, lib.Mock.method(arg), "2")
....
assert.Equal(t, lib.Mock.method(arg), "2")
Share:
10,783

Related videos on Youtube

TechCrunch
Author by

TechCrunch

Updated on October 15, 2022

Comments

  • TechCrunch
    TechCrunch over 1 year

    I'm running a test with multiple parameters in a for loop using go lang testing.

    I ran into a situation where same return value (and first set) is returned every time the mock is called. What I want to be able to do is change the return value for each test when the input is same i.e., same On but different Return in a loop.

    I am using stretchr/testify for mocks. It looks like it will not overwrite already created mock when On is same.

    func TestUpdateContactWithNewActions(t *testing.T) {
        tests := []struct {
            testName  string
            getParams func() *activities.UpdateContactWithNewActionsActivity
            mockError error
        }{
    
            {"UpdateContactWithNewActions with error from contact service",
                func() *activities.UpdateContactWithNewActionsActivity {
                    return fixtures.GetUpdateContactWithNewActionsActivity()
                }, fixtures.Err},
            {"UpdateContactWithNewActions valid",
                func() *activities.UpdateContactWithNewActionsActivity {
                    return fixtures.GetUpdateContactWithNewActionsActivity()
                }, nil},
        }
    
        lib.LoadWithMockClients()
    
        for _, test := range tests {
            test := test
            t.Run(test.testName, func(t *testing.T) {
                lib.MockCSClient.On(
                    "UpdateContactWithNewActions",
                    mock.AnythingOfType("tchannel.headerCtx"),
                    fixtures.UpdateContactWithNewActions).Return(test.mockError)
    
                returnedResult, err := test.getParams().Execute(fixtures.Ctx)
                if test.mockError == nil {
                    // some assertion
                }
                assert.Error(t, err)
            })
        }
    }
    
    • TechCrunch
      TechCrunch over 6 years
      stretchr/testify
    • lmars
      lmars over 6 years
      It looks like the library just appends to an internal list, so when the method gets called it always matches the first return result which was registered. Have you considered using a new mock for each test run?
    • user2823667
      user2823667 about 5 years
      Hey Did you find any solution for this ?
  • Marcello Grechi Lins
    Marcello Grechi Lins about 3 years
    This is the right solution. If you are structuring your test case to validate and force a loop condition or something, you can have an []response and loop through it setting up your mock with .Once() for each response.
  • ArtLatysh
    ArtLatysh over 2 years
    Thanks! This answer must be accepted.