Thursday, September 12, 2013

Using Mockito with JUnit

If you are writing unit tests in order to ensure the proper functionality of your codes, definitely you might face difficulties introduced by dependencies of classes on another. In addition, if the application that you are writing is still not completed then you may not be able to write unit tests for a class that you have already written. For instance, if you have defined an interface and write a code for that interface which, then you have to implement that interface before proceeding with writing a test for the code. In such a situation creating mock objects is a solution. A mock object is an object that can be used in a test instead of a real object. Mock object exposes all the methods that the specified type do but without an implementation logic. Instead methods return values of corresponding return types. For example, if the return type is int, then it returns 0 by default and if the type is void, then the it returns nothing. You can change these values in your mock objects if you are writing such ones. But you can make your life easier by using a mock framework. Mockito is such a java framework that you can use for your unit tests. Here I have explained one example that you can use mocking with Mockito. This is a simple bank application where money is transferred from one account to another. Application consists of following classes.

Account.java
package learn.mockito.bank;

public class Account {

    private int id;
    private int balance;

    public Account(int id, int initBalance) {
        this.id = id;
        this.balance = initBalance;
    }

    public int getId() {
        return id;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }
}

AccountService.java
package learn.mockito.bank;



public interface AccountService {



    public Account getAccount(int id);

    public void updateAccount(Account account);

}

TransactionManager.java
package learn.mockito.bank;

public class TransactionManager {

    private AccountService accountService;
    public TransactionManager(AccountService accountService) {
        this.accountService = accountService;
    }
    public void transferMoney(int senderID, int receiverID, int amount) {
        Account sender = accountService.getAccount(senderID);
        Account receiver = accountService.getAccount(receiverID);
        if (sender.getBalance() >= amount ) {
            sender.setBalance(sender.getBalance() - amount);
            receiver.setBalance(receiver.getBalance() + amount);
            accountService.updateAccount(sender);
            accountService.updateAccount(receiver);
        } else {
            throw new IllegalArgumentException("Sender's balance is not sufficient");
        }
    }
}
In this application AccountService defines the interface for the database transactions. Consider a scenario you need to test TransactionManager at this stage. With the normal approach without mocking it might seem impossible to write a unit test for TransactionManager without an implementation of AccountService. Although any implementation is available then the test depends on the facts such as database connectivity, etc. This reduces the loosely coupled unit tests for TransactionManager class. These kind of troubles in unit tests can be eliminated by using mock objects in your tests. For example the database transactions mentioned above can be mimic using a collection such as Map. Then you can return balance from your data collection and update the collection for update calls. This reduces the test failures and coupling of your tests. Then you can use your unit tests for test the validity of you intended methods despite the changes to the others. In this approach creating mock objects is an additional overhead and it introduces more classes and methods which make things more complex. Mocking frameworks such as Mockito, EasyMock come into play and make our life easier. Lets now try to write a Unit Test for the TransactionManager class with the support of Mockito. The code shown below is a parameterized Unit Test which test transferMoney() method with different values for sender, receiver and amount transferred. You can defin any number of value sets in getParameters() method. This is not related to Mockito and you can write a ordinary test instead and a brief explanation on writing a parameterized test is discussed at the end of this post.

AccountManagerTest,java
package learn.mockito.bank;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import java.util.Arrays;
import java.util.Collection;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@RunWith(value = Parameterized.class)
public class AccountManagerTest {

    private int senderBalance;
    private int receiverBalance;
    private int transferAmount;


    @Parameters
    public static Collection<Integer[]> getParameters() {
        return Arrays.asList(new Integer[][]{ {1000, 1000, 500}, {1500, 2000, 1000}, {3000, 4000, 2000} });
    }

    public AccountManagerTest(int senderBalance, int receiverBalance, int transferAmount) {
        this.senderBalance = senderBalance;
        this.receiverBalance = receiverBalance;
        this.transferAmount = transferAmount;
    }

    @Test
    public void testTransferMoney() {
        Account sender = new Account(1, senderBalance);
        Account receiver = new Account(2, receiverBalance);
        AccountService as = mock(AccountService.class);
        when(as.getAccount(1)).thenReturn(sender);
        when(as.getAccount(2)).thenReturn(receiver);
        TransactionManager tm = new TransactionManager(as);
        tm.transferMoney(1, 2, transferAmount);
        Assert.assertEquals(sender.getBalance(), senderBalance - transferAmount);
        Assert.assertEquals(receiver.getBalance(), receiverBalance + transferAmount);
    }
}
As dependencies I have used java libraries.
  • mockito-all-1.9.5
  • junit-4.11
You can add these dependencies to your project in the way your IDE support it and hence discussion of it is omitted. Here I have created a mock object of AccountService which is not implemented. For this we have to just call mock() method passing the class that we need to mock as a parameter. mock() can be called directly as it is imported statically. Then I have defined what AccountService should return when the getAccount() is called. You can easily understand it by looking at the code. The remaining part is JUnit assertions which assert whether the transactions are done properly. You can start using mockito in this level and proceed with more complex usages. Now run your test and you will get your test passed like below in Intellij Idea for example.


Creating a parameterized test. You can create Unit Tests with more than one input parameter set for a particular test. These are known as parameterized tests. In order to create a parameterized test, the following conditions must be satisfied.
  • Annotate the test class with @RunWith annotation with Parameterized.class as the value.
  • Define instance variables which are used in tests.
  • Define a public static method which is annotated with @Parameters. This is a no argument method and return type of it is java.util.Collection. This method must return your test parameter as an Arrays of identical length.
  • Declare a public constructor for test class which accepts the number of arguments returned by parameter method and instantiate instance variables.
Thank you.