Skip to content
This repository was archived by the owner on Jun 27, 2023. It is now read-only.

Default expectations and override #137

Closed
ybbus opened this issue Dec 18, 2017 · 8 comments
Closed

Default expectations and override #137

ybbus opened this issue Dec 18, 2017 · 8 comments
Labels
status: needs more info This issue need more information from the author. type: question

Comments

@ybbus
Copy link

ybbus commented Dec 18, 2017

When you do a lot of tests with different dependencies it would be great if you could do the following:

Define a default behaviour (e.g.)

mockA.Expect().Function1().AnyTimes().Return(1)
mockA.Expect().Function2().AnyTimes().Return(2)

mockB.Expect().Function1().AnyTimes().Return(1)
mockB.Expect().Function2().AnyTimes().Return(2)

And most of the time in a single test you want to test a behaviour of your component if one of the default functions return something else (e.g. an error)

Therefore it would be great to override an expectation with a new one (or at least to clear expectations for a function)

So you could define a default behaviour of all functions (e.g. success case) and be able to override single functions that should behave different in specific tests.

Or is there a best practive for this?

@zjshen14
Copy link

I have similar question. By default I want

mockObj.EXPECT().Foo().Return(1).AnyTimes()

And in some test cases, I want to enforce a particular time

mockObj.EXPECT().Foo().Return(1).Times(1)

However, given the first is defined, the second one seems not to work

@andrewmains12
Copy link

Probably a bit late here, but for anyone coming upon this in the future, one pretty reasonable workaround that I just used here involves leveraging the order of precedence as-is to set defaults. That is, since earlier expectations take precedence over later ones, overrides work if you set them before filling in defaults:

import (
	"testing"

	"github.com/magiconair/properties/assert"

	"github.com/golang/mock/gomock"
)

// N.B.: code assumes mocks have already been generated, e.g. with:
// mockgen -source obj.go > mock_obj_test.go
type Obj interface {
    Foo() string
}

// applyDefaults adds sensible default expectations to mockObj. Any expectations already present will
// not be overridden.
func applyDefaults(mockObj *MockObj) {
	mockObj.EXPECT().Foo().Return("default")
}

func TestObj(t *testing.T) {
	ctrl := gomock.NewController(t)
	mockObj := NewMockObj(ctrl)
	mockObj.EXPECT().Foo().Return("override")

	applyDefaults(mockObj)
	assert.Equal(t, "override", mockObj.Foo())
}

@codyoss
Copy link
Member

codyoss commented Oct 12, 2019

I think the last comment sums up a way to do this quite well. Is it reasonable to close this issue @ybbus?

@codyoss codyoss added status: needs more info This issue need more information from the author. type: question labels Oct 12, 2019
@codyoss codyoss closed this as completed Nov 10, 2019
@reversekick
Copy link

I think the last comment sums up a way to do this quite well. Is it reasonable to close this issue @ybbus?

Sorry its not clear how the last comment addresses the issue. The last comment seems to reiterate the problem statement again i.e. once EXPECT is defined you cannot change its definition while the ask is to allow change of the definition
e.g. to test both positive and negative flows of a function I want

// assuming true is the positive case
mockA.EXPECT().Function1().AnyTimes().Return(true)

// now to test a negative flow I want it to return false
mockA.EXPECT().Function1().AnyTimes().Return(false)

but this second EXPECT doesnt seem to take affect ?

@gautamrege
Copy link

@reversekick The way I solved this was a hint from StackOverflow -
https://stackoverflow.com/questions/51029598/mocking-same-functions-with-different-response-many-times-one-after-the-other-in/51031713

TLDR: Don't use AnyTimes() in EXPECT and make that EXPECT call get discarded after single use.

Use a "prepareTest" function that can be called before the test case to set the EXPECT statement for positive or negative test case. This makes sense because AnyTimes() will instruct GoMock to return the same result multiple times.

@jameshalsall
Copy link

I don't think this is caused by using AnyTimes(), e.g.

My default case, where I create my struct with dependencies for the test

myMockDependency.EXPECT().Something(gomock.Any()).Times(1).Return(nil)

In my test where I want to change this behaviour to make it return something different:

myMockDependency.EXPECT().Something("foo").Times(1).Return(errors.New("something bad happened"))

What I see:

missing call(s) to *handler.MockmyMockDependency.Something(is equal to foo)

It seems like this could be solved by adding a function on the generated controllers to clear any expectations?

@rcw5
Copy link

rcw5 commented Nov 24, 2022

I came across this issue as I was looking for ways to simplify my error condition tests, which essentially involve constructing mocks such that in test1 call1() fails, in test2 call1() succeeds and call2() fails, and so on. Being able to set sensible defaults then override on a per-test basis avoids a lot of duplication and reduces the pain of making changes to method signatures.

I wasn't able to get the example from @andrewmains12 working, so I suspect something in Gomock has changed since it was first written. Gomock now expects both calls to be received, and so fails with the error:

    /tmp/foo/controller.go:137: missing call(s) to *main.MockObj.Foo() /tmp/foo/main_test.go:14
    /tmp/foo/controller.go:137: aborting test due to missing call(s)

A simple fix is to append .AnyTimes() on the default case, i.e:

func applyDefaults(mockObj *MockObj) {
	mockObj.EXPECT().Foo().Return("default").AnyTimes()
}

This change would loosen a test slightly - as it's no longer asserting the exact number of calls to the dependency - but generally speaking failing to call the mock the right number of times would cause a subsequent expectation to fail

@bigsheeper
Copy link

The best way I think:

  1. clear all expectations before
	mocker1.ExpectedCalls = suite.mocker1.ExpectedCalls[:0]
  1. set expectations again
        mocker1.EXPECT().Foo().Return(xxx)

# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
status: needs more info This issue need more information from the author. type: question
Projects
None yet
Development

No branches or pull requests

9 participants