15

When trying to use Mockito with Spring, by creating the Mock object via a bean declaration...

<bean id="accountMapper" class="org.mockito.Mockito" factory-method="mock"> 
    <constructor-arg value="org.example.persistence.mybatis.mappers.AccountMapper" /> 
</bean>     

...I found some strange behavior when calling Mockito.when multiple times without reseting the Mock object, for example:

Mockito.when(this.accountMapper.createBadGrammarException()).thenThrow(new BadSqlGrammarException("Bla", null, new SQLException()));

As soon as this code (the "Mockito.when") is called multiple time during the test (on the same mock), the tests fails with an error (BadSqlGrammerException even if this exception was what was actually expected - I do get a failure if I don't throw the exception, and throwing it manually works fine). Is this expected behavior? Mockito seems to suggest creating a new mock every time, which would mean creating the DAO for each method...?

What exactly happens when I call the Mockito.when method two times? How should the mock react? Replace the behavior? Ignore it? Unfortunately most searches only yield results for how to return different results for multiple calls to the method itself, but not what is to be expected for multiple calls to Mockito.when...

I'm simply trying to understand Mockito and best practices here, because going with something just because it SEEMS to works seems to be a bad idea...

Florian Schaetz
  • 10,454
  • 5
  • 32
  • 58
  • Is your Spring context reloaded before each test? If not, this behavior is normal: the accountMapper mock is then a singleton, so each method you stub will stay stubbed until the Spring context is destroyed. Could you copy paste your test class source code? – Damien Beaufils Jul 20 '15 at 09:03
  • Use `doThrow`. I'll turn this into a full answer if I have time later. – Dawood ibn Kareem Jul 20 '15 at 09:05
  • Thanks, I'm looking forward to it. `Mockito.doThrow(new BadSqlGrammarException("Bla", null, new SQLException() ) ).when( this.accountMapper ).createBadGrammarException();` seems to work quite fine. I read about doThrow but dismissed it, since my "createBadGrammarException" is not a void method and the Mockito doc suggested that for them. Thanks for taking the time, hopefully the answer will help me understand the "why". – Florian Schaetz Jul 20 '15 at 09:19
  • 1
    The best practice is indeed to create a mock for each test. – fge Jul 20 '15 at 09:27
  • Any tips regarding on how to do that with Spring and not adding getters/setters for the interface? Wouldn't mind creating a new mock for each test method, I would like a way that doesn't clutter the actual code... – Florian Schaetz Jul 20 '15 at 09:31
  • 1
    You did a very poor research in any case. Answer are [here][1], and [here][2] [1]: http://stackoverflow.com/questions/8088179/using-mockito-with-multiple-calls-to-the-same-method-with-the-same-arguments [2]: http://stackoverflow.com/questions/4216569/how-to-tell-a-mockito-mock-object-to-return-something-different-the-next-time-it – Vargan Jul 20 '15 at 09:35
  • I am sorry, but no, you just got the question I was asking wrong: I do NOT want to know aynthing about how to return different values (or throw different exceptions) when calling a method on a mock multiple times. I already know that. I was asking what happens if you call the actual method "Mockito.when" multiple times on the same mock (without resetting it first) with the same parameters. So, no, neither of your links has anything do with my question, sorry. I tried to prevent that from happening by explaining it in my question, but obviously I wasn't clear enough, sorry. – Florian Schaetz Jul 20 '15 at 09:39

2 Answers2

42

One of the problems with Mockito.when is that the argument you pass to it is the expression that you're trying to stub. So when you use Mockito.when twice for the same method call, the second time you use it, you'll actually get the behaviour that you stubbed the first time.

I actually recommend NOT using Mockito.when. There are many traps that you can fall into when you use it - quite a few cases when you need some other syntax instead. The "safer" alternative syntax is the "do" family of Mockito methods.

doReturn(value).when(mock).method(arguments ...);
doThrow(exception).when(mock).method(arguments ...);
doAnswer(answer).when(mock).method(arguments ...);

So in your case, you want

doThrow(new BadSqlGrammarException(??, ??, ??)).when(accountMapper).createBadGrammarException();

If you are starting out with Mockito, then I recommend that you learn to use the "do" family. They're the only way to mock void methods, and the Mockito documentation specifically mentions that. But they can be used whenever Mockito.when can be used. So if you use the "do" family, you'll end up with more consistency in your tests, and less of a learning curve.

For more information about the cases when you must use the "do" family, see my answer on Forming Mockito "grammars"

Community
  • 1
  • 1
Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
  • Thanks. While I'm still a little bit unsure about the specific details of this exact problem, it's at least a point to start. As I'm also still figuring out the best practices for testing with Spring, etc. I've still some ground to cover. – Florian Schaetz Jul 20 '15 at 10:31
  • Ah, seems I had some kind of enlightenment: When writing when(...).thenThrow(...) two times, the 2nd time actually throws the exception defined in the first time (because the 2nd time CALLS the method itself). This is why my test failed probably. With the other way round, do(...).when(...) this does not seem to be the case. Strange, I'll have to debug into the whole thing some day ;-) – Florian Schaetz Jul 20 '15 at 13:49
  • This tip is a life saver! – Tommy Jul 14 '17 at 10:18
  • I wish I could upvote this more. Not using `when()` again. I ran into the same issue where `when()` will complain about my stubbing, as soon as I switched it to `doReturn` with specific inputs it worked! Thanks so much! – mauricioSanchez Jun 18 '19 at 21:31
3

The simple answer is:
when you write Mockito.when(object.fooMethod()).then() then fooMethod() is actually called.
Another point is that we can't observe it first time, because it called on mocked object. But when we write when for the second time then we have some behavior for fooMethod() (we set it previously, in your case it Exception).

To check this better you can spy object:

Bar spyBar = Mockito.spy(Bar.class)
when(spyBar.fooMethod()).then()...

and fooMethod() will be actually called.

Vova Yatsyk
  • 3,245
  • 3
  • 20
  • 34