Mockito UnfinishedStubbingException thrown on a JUnit test case which calls a void ServiceImplementationLayer method

It seems that there is a some confusion about mocking going on here.

With the code you have, you would look to write a test for your AccountServiceImpl but mocking out the AccountDao. Instead of using the real DAO which talks to a database, you use a mock DAO which you can set up to return certain values when certain methods are called. You can also query the mock to find out how many times it was called and with what values.

Mocking domain objects

In your test it seems you’ve also chosen to mock out the Account class. You haven’t included your Account class in your question but I’m guessing it just has getters and setters. If so, I wouldn’t bother mocking it out. Instead of using a mock account, use a real one. Replace the line

        Account account = Mockito.mock(Account.class);

with

        Account account = new Account();

That way you can call the getters and setters on account and they will behave as you expect. If you insist on using a mock account, you would have to use Mockito to implement the getters and setters:

        when(account.getId()).thenReturn(...)        
        when(account.getBalance()).thenReturn(...)
        // ...

You also call account.setId() on your mock account. This will do nothing because you haven’t told Mockito what to do when you call .setId() on a mock account. Calling account.setId() on a real account will set the ID.

You don’t have to mock out every single class other than the one being tested. Whether you do depends on what these other classes do. If another class has some complicated logic, or communicates with databases, filesystems, networks, etc., then it can be useful to mock it out, so that your test doesn’t have to worry about either the complicated logic or the communication with external systems. In this case, however, let’s not bother mocking Account because it doesn’t do anything complicated.

Mock setup

When creating a mock, all void methods will do nothing and all other methods will return false, zero or null as appropriate. If you want them to do something else, you need to set them up.

Your service uses your DAO to load an account from the database. You need to set up the mock accountDao to return account when given its ID. Do this by adding the following line to your test before the line that calls service.makeDeposit(...):

        when(daoMock.selectAccountByAccountId(1)).thenReturn(account);

But what about the updateAccountBalance() method? By default, that will do nothing, and you appear to be attempting to set it up to do nothing, which it already does. You can delete the line that attempts to set up this method, as it would achieve nothing. Later on, we will look at verifying this method, i.e. asserting that it was called.

Mocking inside of mocking

You were getting an error with this line:

        doNothing().when(daoMock).updateAccountBalance(account.getBalance() + amount, accountNumber)

When setting up one mock, you can’t call a method on another mock. Why would you need to? If it’s a mock then you should already have set up the mock method to return a certain value, so just use that value instead. In other words, instead of writing

        // if account is a mock...
        when(account.getBalance()).thenReturn(10.00);
        doNothing().when(daoMock).updateAccountBalance(account.getBalance() + amount, accountNumber)

just write

        // if account is a mock...
        when(account.getBalance()).thenReturn(10.00);
        doNothing().when(daoMock).updateAccountBalance(10.00 + amount, accountNumber)

In this line, you are attempting to set up daoMock and are calling account.getBalance(). If account is also mock, this will cause the problem.

The reason this causes a problem is due to the way Mockito works. Mockito can’t see your source code, all it sees is calls to its own static methods and calls to mocks. The line

        doNothing().when(daoMock).updateAccountBalance(account.getBalance() + amount, accountNumber)

causes the following sequence of interactions:

  1. Static Mockito method doNothing() called,
  2. when() method of whatever object doNothing() called,
  3. account.getBalance() method called,
  4. updateAccountBalance() method of the mock DAO called. (We can’t call a method until we’ve evaluated all of the arguments.)

To Mockito, the first three steps of this are indistinguishable from the following:

        doNothing().when(daoMock);
        when(account.getBalance()).thenReturn(...);

In this case, it is clear that we haven’t finished setting up daoMock. We would expect an exception in this case.

Mockito’s syntax leads to clear and expressive mocking, but if care isn’t taken you can sometimes end up in situations like this.

We’ve already decided to get rid of the line that caused this problem, so this section is more for information and understanding than anything else.

Verification

Next, we look at the following lines:

        //run
        service.makeDeposit(amount, account.getAccountId());

        //verify
        verify(service, times(1)).makeDeposit(amount, account.getAccountId());

What does this do? You call the makeDeposit method, and then you verify that you called the makeDeposit method. There’s really no need to make this verification: you can plainly see that this method is called three lines above.

Generally, you don’t verify methods on the class being tested. Instead, you verify methods on the mocks that your class called. What you want to do instead is to verify that the relevant method on the mock DAO got called with the expected values:

        verify(daoMock, times(1)).updateAccountBalance(account.getBalance() + amount, account.getAccountId());

You can also get rid of the call to Mockito.spy(...). I’ve never used spies myself, and I don’t see the need for them here. Replace the line

        service = Mockito.spy(new AccountServiceImpl());

with

        service = new AccountServiceImpl();

In conclusion

There’s a fair amount here, and hopefully at least some of it makes sense and gives you a better understanding of what’s going on. I made the changes above to your test and I got it to pass.

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top