golang / mock

GoMock is a mocking framework for the Go programming language.
Apache License 2.0
9.26k stars 608 forks source link

Adding method to Controller allowing to check whether all expected calls have been made #617

Open FlorianLoch opened 2 years ago

FlorianLoch commented 2 years ago

I want to be able to check whether all expected calls have been made before Controller#Finish() gets invoked as the code being tested is running asynchronously and I can only approximate when all calls will have been made.

This addition should allow it to check whether all expected calls towards the mock(s) have been made in an idempotent way. This is useful when testing asynchronous/non-deterministic things that aren't fully under the developers control (e.g., in end-2-end tests).

If necessary I can elaborate on my specific testing scenario.

Having such a method allows the user to write something like

require.Eventually(t, mockCtrl.AllExpectedCallsSatisfied, 60*time.Second, 100*time.Millisecond)

instead of having to wait for an approximated amount of time (which slows down the tests unnecessarily) in order to make sure all expected calls have been satisfied before Controller#Finish() gets invoked.

Why is this needed? Because right now AFAIU I have to add a sleep for an amount of time I approximate to be enough for my system to be done with its work. This is not nice for several reasons:

In order to check how feasible the implementation would be I already coded a proposal that I would be happy to open a PR for: https://github.com/golang/mock/compare/main...FlorianLoch:check-expectation-fulfilled?expand=1

FlorianLoch commented 1 year ago

This issue (and the linked PR) ist open for more than 9 months now. Could I please get some official feedback? That would be nice :)

tkrop commented 1 year ago

Have you considered to use a WaitGroup as described in https://medium.com/@poy/gomock-and-go-routines-6a7c01d989d5 for signalling completion. I know it is actually a bit more difficult since you also need to unlock your waiting test thread on failures, if you do not want to timeout, which totally disturbs the user experience.

FlorianLoch commented 1 year ago

That isn't a bad idea as a work around - but I guess having this addition in place would be simpler and more intuitive.

It's a little disappointing, that there is no actual activity on this issue and the related PR 🤷

cwmos commented 1 year ago

Here is a different use case for this feature that I would really like support for. Suppose I have this code I want to test:

type Callback interface {
    Callback(int)
}

func PerformCallback(cb Callback, par int) {
    cb.Callback(par)
}

Then I would like to do it like this:

func TestPerformCallback(t *testing.T) {
    ctrl := gomock.NewController(t)
    cb := NewMockCallback(ctrl)

    cb.EXPECT().Callback(1)
    PerformCallback(cb, 1)

    // Here it would be nice to assert that all expectations setup so far have been met

    cb.EXPECT().Callback(2)
    PerformCallback(cb, 2)
}