Optional calls in GoMock

2.5k views Asked by At

I want to use GoMock to test some code, without coupling my tests too tightly to the actual implementation of the thing under test. But GoMock seems to require that I be able to say in advance exactly what calls the code under test will make, even if that's not part of the contract I'm trying to test. Is there a way around this?

For example, say I'm testing some validation logic, where the code under test is required to take a Widget named mockedObject, and return false if either mockedObject.CheckConditionA() or mockedObject.CheckCondition1() returns false, and to return true otherwise. One correct implementation is:

func UnderTest(mockedObject *Widget) bool {
    if !mockedObject.CheckConditionA() {
        return false
    }
    return mockedObject.CheckCondition1()
}

Another is:

func UnderTest(mockedObject *Widget) bool {
    if !mockedObject.CheckCondition1() {
        return false
    }
    return mockedObject.CheckConditionA()
}

A third is:

func UnderTest(mockedObject *Widget) bool {
    cA := mockedObject.CheckConditionA()
    c1 := mockedObject.CheckCondition1()
    return cA && c1
}

And a fourth is:

func UnderTest(mockedObject *Widget) bool {
    if TodayIsAnAlternateTuesday() {
        return mockedObject.CheckCondition1() && mockedObject.CheckConditionA()
    }
    return mockedObject.CheckConditionA() && mockedObject.CheckCondition1()
}

My test shouldn't have to care which of these is being used; I just want to check that the contract is fulfilled.

How do I set up the mocks for a test like this, where the code under test is allowed but not required to call a method?

For the test for the case where CheckConditionA() will return false, I need to set up something like mockedObject.EXPECT().CheckConditionA().Return(false). But some of these implementations will also require a mockedObject.EXPECT().CheckCondition1().Return(true) in order to work, or my test will fail due to unexpected calls. On the other hand, some will fail if I provide that EXPECT, due to missing calls.

Does GoMock just not support writing this kind of test? If not, what else could I be using that would let me test this function without constructing a real live Widget?

1

There are 1 answers

0
interfect On

This question gets at the problem in a general context, and following this answer to this answer shows that in this case you do not want Mocks; you actually want Stubs, which are mocks that don't insist on being called but if called will return the right things.

The GoMock README actually has a whole section on building stubs instead of mocks, the upshot of which is that you need to use AnyTimes() at the end of the EXPECT() call chain to make it optional.

mockedObject.EXPECT().CheckConditionA().Return(false)
mockedObject.EXPECT().CheckCondition1().Return(true).AnyTimes()

Unfortunately, that README section contains exactly one title and two code examples, with no text about what a stub is or why someone would want one, which means if your first exposure to mock-based testing is through GoMock you are not going to figure this out very quickly. The main GoMock docs have a one-line description of AnyTimes(), but nothing about stubs as a concept. (Or about mocks as a concept for that matter; they just sort of assume you've heard of them.)