mockito icon indicating copy to clipboard operation
mockito copied to clipboard

Mocks created with GenerateMocks do not throw on missing stub when the function returns a Future<void>

Open Barabas5532 opened this issue 2 years ago • 5 comments

A call on a function with the signature Future<void> returnsFutureVoid() => Future.value(); with no expected calls set up with when will not cause a MissingStubError.

I have read through the README, the FAQ and some of the test cases. From what I understand, this error should always be thrown for mocks generated with GenerateMocks. I couldn't find any documented problems with functions that return a future. I was not able to reproduce the issue for the mock in the mockito_test.dart file.

I'll open a pull request with a new test case to demonstrate the issue. If this is a valid bug, I would appreciate any advice on how to fix it.

Barabas5532 avatar Nov 03 '22 11:11 Barabas5532

I've just noticed that this also happens if the function returns void, and there are existing tests that expect no MissingStubError:

    test('a method which returns Future<void> can be called without stubbing',
        () {
      expect(() => foo.returnsFutureVoid(), returnsNormally);
    });

    test('a method which returns Future<void>? can be called without stubbing',
        () {
      expect(() => foo.returnsNullableFutureVoid(), returnsNormally);
    });

If this really the correct behaviour then I think there is a bug in the documentation here:

https://github.com/dart-lang/mockito/blob/094d07cdceca1f9f4ef863fb57045295298e8682/NULL_SAFETY_README.md?plain=1#L147

When a mock class generated with @GenerateNiceMocks receives a method call which does not match any method stub created with the when API, a simple, legal default value is returned. This is the default and recommended "missing stub" behavior.

There is a "classic" "missing stub" behavior, which is to throw an exception when such a method call is received. To generate a mock class with this behavior, use @GenerateMocks:

This makes it sound like the call should throw, because the test never used when.

Barabas5532 avatar Nov 03 '22 12:11 Barabas5532

Yes it is intentional for functions that return both void and Future<void> and we should document that.

srawlins avatar Nov 03 '22 13:11 srawlins

What's the rationale behind this behaviour? That void and Future<void> can only ever return one kind of value, so the missing mock doesn't matter? (Not the case for Future<void>?, also they all can throw...)

jakobleck avatar Nov 15 '22 11:11 jakobleck

Yeah that's the general idea. We're implementing the idea that it's a pain to make people spell out in tests, "when this void-returning method is called with whatever, it should return, big surprise, nothing." You're right that there are multiple behaviors such a call could do, like throw anything. 🤷 It's just an implementation choice.

srawlins avatar Nov 15 '22 17:11 srawlins

I think it would be more consistent to make function that return void throw, just like functions returning any other type. However this seems like a breaking change, since there could be tests relying on the current behaviour.

I spotted this problem when the code I was testing was calling a function returning Future<void> with the wrong arguments. This caused the stub set up with when to not run, making the test to fail in a weird way due to the side effect of the stub not getting executed. I managed to work around the issue by calling verify with the same arguments used to set up when. This throws straight away, and fail the test with a good error message.

Barabas5532 avatar Nov 15 '22 21:11 Barabas5532