Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dart Mockito when first, second, ... n calls, return different value #260

Closed
tolotrasamuel opened this issue Jun 7, 2020 · 20 comments
Closed

Comments

@tolotrasamuel
Copy link

In Java Mockito, you can Mock a different returned value for each call like this:

when(myMock.doTheCall())
   .thenReturn("You failed")
   .thenReturn("Success");

Or like this:

when(myMock.doTheCall()).thenReturn("Success", "you failed");

Source: https://stackoverflow.com/questions/11785498/simulate-first-call-fails-second-call-succeeds

I wonder how to achieve the same in Dart Mockito. I could not find a documentation for that

@srawlins
Copy link
Member

srawlins commented Jun 7, 2020

There is not a built-in way to do this. See my response here. We could add an API like thenReturnInOrder.

@matuella
Copy link

matuella commented Sep 27, 2020

I've also faced the same problem where I needed to return different values to the same call - in the same test. @srawlins your response did the trick for me, but I think it would be way more readable if there is some API like the one you suggested.

@ryanelliott-wk
Copy link

An api like thenReturnInOrder would be great but it would also be nice to be able to throw in an exception. I am wanting to test that my method retries when another method throws. Java's Mockito would allow me to set that up like this:

when(myMock.doTheCall())
   .thenThrow(Exception("You failed"))
   .thenReturn("Success");

See the example in the docs here: https://javadoc.io/static/org.mockito/mockito-core/4.2.0/org/mockito/Mockito.html#10

@srawlins
Copy link
Member

@danielgomezrico do you have an API in mind?

@danielgomezrico
Copy link
Contributor

danielgomezrico commented Sep 12, 2022

Yes, I checked with this small closure, and it works:

extension MultipleExpectations<T> on PostExpectation<T> {
  void thenAnswerInOrder(List<T> bodies) {
    final answers = Queue.of(bodies);
    thenAnswer((_) => answers.removeFirst());
  }
}

And then use it like:

when(mock).thenAnwserInOrder(['First call', 'second call']);

I could open a PR with that, maybe we could add another one that accepts lambdas and return the execution to allow throwing exceptions? something like:

when(mock).thenAnwserInOrder([
    () => 'First call', 
    () => 'second call', 
    () => throw 'BOOOM']
);

@srawlins
Copy link
Member

thenAnswerInOrder should be thenReturnInOrder, right?

Other than that, I like the API. We'll have to make sure the implementation works with other thenReturn calls, and has a defined behavior for then n+1th call.

@danielgomezrico
Copy link
Contributor

And why return works better? I thought it would fit better because in the internals it uses thenAnswer

@danielgomezrico
Copy link
Contributor

I have one question... what should it return, then the function was called more times than the number of times it was declared? it should throw an exception or what?

@srawlins
Copy link
Member

And why return works better? I thought it would fit better because in the internals it uses thenAnswer

They do, but I think it is more important for the name to follow the pattern of other interfaces (thenReturn), from the user's perspective, and not really depend on the implementation.

@srawlins
Copy link
Member

I have one question... what should it return,

void seems fine.

then the function was called more times than the number of times it was declared? it should throw an exception or what?

I think it should follow the current behavior, which is to use a defaultResponse. Hopefully you don't have to handle anything in your implementation, but it should be tested.

@jaoassy
Copy link

jaoassy commented Oct 13, 2022

Is there any news about this topic? Is there any expectation of thenReturnInOrder being implemented?

I think that many of us that are coming to Dart from Java/Kotlin will have this need.

@srawlins your workaround works fine though.
Thanks for that!

@danielgomezrico
Copy link
Contributor

I apologize for taking too long, I will start working on it this week 🤓

@danielgomezrico
Copy link
Contributor

I think it should follow the current behavior, which is to use a defaultResponse. Hopefully you don't have to handle anything in your implementation, but it should be tested.

@srawlins can you guide me in that defaultResponse thing? I already opened a PR in: #597 WDYT?

@yanok
Copy link
Contributor

yanok commented Jun 12, 2023

Ok, so I reviewed the PR without looking at this bug. So I overlooked the requirement to return to the default response once we are out of provided responses. Returning back to default seems doable, but I'm undecided if it would actually follow the previous behavior, since all of thenReturn/thenAnswer and thenThrow will stay there unless re-set.

@danielgomezrico
Copy link
Contributor

@yanok can you reword your comment? I don't understand what do you mean.

@yanok
Copy link
Contributor

yanok commented Jun 15, 2023

I mean I've read the previous discussion:

then the function was called more times than the number of times it was declared? it should throw an exception or what?

I think it should follow the current behavior, which is to use a defaultResponse. Hopefully you don't have to handle anything in your implementation, but it should be tested.

and realized we didn't do what @srawlins requested there: currently if we run out of provided responses we don't return to using defaultResponse and unconditionally throw an exception instead.

@srawlins Sam, do you still think we should try to get back to defaultResponse? I think it is possible, but will be a bit messy. Or can we just close this?

@srawlins
Copy link
Member

@srawlins Sam, do you still think we should try to get back to defaultResponse? I think it is possible, but will be a bit messy. Or can we just close this?

I this this behavior is fine. I don't think I strongly preferred defaultResponse earlier. This seems like something we can definitely decide for ourselves as to what is the best API. It doesn't seem egregious to me to require n specified responses for n calls. For more complicated logic, can thenAnswer with an external counter/signal.

@yanok
Copy link
Contributor

yanok commented Jun 30, 2023

ok, thanks Sam, let's close this as implemented then.

@muzzah
Copy link

muzzah commented Sep 3, 2024

@srawlins Im trying to do this with Futures. My method im mocking returns a future and in the first invocation I am trying to throw an error but then in the second invocation I am returning a valid value. I seem to not be able to have the framework work with this with some errors being thrown.

the code being tested is as following

try {
     await someMethod.underTest()
} catch(error) {

}

And in my test i have

int callCount = 0;
 when(someMethod.underTest()).thenAnswer(
          (_) =>
              [Future<SomeType>.error(Exception()) , Future<SomeType>.value(instanceOfSomeType)][callCount++]);

When the test runs, the first invocation is throws and is handled correctly, on the second invocation the code passes the method and execution continues normally but but I get the following output in the logs and the test fails

══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following _Exception was thrown running a test:
Exception

When the exception was thrown, this was the stack:
[Long stack trace]

════════════════════════════════════════════════════════════════════════════════════════════════════

Test failed. See exception logs above.

I know support for thenAnswerInOrder has not been implemented but is there some incompatibility to make it work thenAnswer?

@muzzah
Copy link

muzzah commented Sep 3, 2024

It seems if I rework the code to not use arrays and just an if/else block it works, so Im not sure if Im doing something wrong by returning futures in that way or there is a bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants