Sunday, March 10, 2013

Junit4,Mockito and PowerMock


Purpose of Unit Testing

Whenever the topic of unit testing is raised.We will see lots of weird faces.Good number of developers will say why we need to write unit tests.Its waste of time.If any issues will come, we will debug it and resolve it.

But I feel that unit testing is important.Reasoning behind it is as follows:-
  • Everyone feels that their implementation is fine.Various components in the code are loosely coupled and independent.However its during test case writing time that we can make out how good the code is.It provides an insight about the quality of code.
  • Its rare that we write the perfect code.Any code goes through refactoring cycles.Having unit test comes handy.We can validate whether refactoring has not broke any existing workflow or not.
  • Good unit test case is like a developer guide.Other developers can look at test case to figure out how they can integrate their code.

So Unit testing is like going to Gym.We know its good for us.But it just takes an extra effort to reach towards it. But just like it keeps you fit and healthy.It gives you confidence that your implementation is doing what it is supposed to do.




JUnit4

JUnit is the standard unit testing library for the Java. JUnit 4 simplifies testing by using annotation feature provided by Java 5 rather than relying on subclassing, reflection, and naming convention.

Annotations introduced in JUnit4 are as follows:-
  • @Test
  • @BeforeClass
  • @AfterClass
  • @Before
  • @After
  • @Parameters
Besides this we can test exceptions and time taken by the tests .

@Test
@Test is a replacement of both TestCase class and convention "test" which we prefix to every test method.Any method which we want as test method should have this annotation.

@Before and @After
In case we want to execute something before and after the test case.We annotate methods with these annotations. So in case we have three test methods.Then methods annotated with @Before and @After will be invoked three times.

@BeforeClass and @AfterClass
In case we want to execute something only once for a test class. Then we annotate static methods with @BeforeClass and @AfterClass.So if your test class has five methods.Then these methods will be executed only once

@Parameters
Lets suppose you want to run a test for different inputs.We can specify @Parameters annotation for that.

Use Case of a banking system
To understand the concepts better,lets take  a use case of a banking system.


Use case has following classes.These are:-
  • BankAccount model class containing account number,contact number,address,name and others
  • Bank class which contains list of bank accounts along with bank name ,address and others.
  • IBankOperations interface containing general methods for deposit, withdrawl and others.
  • BankOperationsImpl class implementing IBankOperations having bank as the reference.
BankAccount.java
Model Class for BankAccount containing account number,address and others.


package com.kunaal.model;

/**
 * Model class for BankAccount
 *
 * @author ktrehan
 */
public class BankAccount {

    // Variable for bank account number
    private String acctNumber;

    // Variable for account holder name
    private String name;

    // Variable for account holder address
    private String address;

    // Variable for account holder contact number
    private String contactNumber;

    // Variable for account balance
    private Double currBalance;

    /**
     * Parameterized constructor
     *
     * @param acctNumber
     * @param name
     * @param address
     * @param contactNumber
     */
    public BankAccount(String acctNumber, String name, String address,
        String contactNumber, Double currBalance) {
    this.acctNumber = acctNumber;
    this.name = name;
    this.address = address;
    this.contactNumber = contactNumber;
    this.currBalance = currBalance;
    }

    /**
     * @return the acctNumber
     */
    public String getAcctNumber() {
    return acctNumber;
    }

    /**
     * @param acctNumber
     *            the acctNumber to set
     */
    public void setAcctNumber(String acctNumber) {
    this.acctNumber = acctNumber;
    }

    /**
     * @return the name
     */
    public String getName() {
    return name;
    }

    /**
     * @param name
     *            the name to set
     */
    public void setName(String name) {
    this.name = name;
    }

    /**
     * @return the address
     */
    public String getAddress() {
    return address;
    }

    /**
     * @param address
     *            the address to set
     */
    public void setAddress(String address) {
    this.address = address;
    }

    /**
     * @return the contactNumber
     */
    public String getContactNumber() {
    return contactNumber;
    }

    /**
     * @param contactNumber
     *            the contactNumber to set
     */
    public void setContactNumber(String contactNumber) {
    this.contactNumber = contactNumber;
    }

    /**
     * @return the currBalance
     */
    public Double getCurrBalance() {
    return currBalance;
    }

    /**
     * @param currBalance
     *            the currBalance to set
     */
    public void setCurrBalance(Double currBalance) {
    this.currBalance = currBalance;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
    return "BankAccount [acctNumber=" + acctNumber + ", name=" + name
        + ", address=" + address + ", contactNumber=" + contactNumber
        + ", currBalance=" + currBalance + "]";
    }

}



Bank.java
Model class for Bank containing list of bank accounts,bank address and others.

package com.kunaal.model;

import java.util.ArrayList;
import java.util.List;

/**
 * Model class for Bank
 * 
 * @author ktrehan
 */
public class Bank {

        // Variable for bank name
        private String name;

        // Variable for bank address
        private String address;
    
    // Variable for bank contact number
        private String contactNumber;
    
        // Variable for list of bank accounts
        private List<BankAccount> acctList = new ArrayList<BankAccount>();
    
        /**
         * Parameterized constructor
         * 
         * @param name
         * @param address
         * @param contactNumber
         * @param acctList
         */
        public Bank(String name, String address, String contactNumber,
            List<BankAccount> acctList) {
            this.name = name;
            this.address = address;
            this.contactNumber = contactNumber;
            this.acctList = acctList;
        }
    

        /**
         * Parameterized constructor
         * 
     * @param name
     * @param address
     * @param contactNumber
     */
    public Bank(String name, String address, String contactNumber) {
            this.name = name;
            this.address = address;
            this.contactNumber = contactNumber;
    }
        
        
        /**
         * @return the name
         */
        public String getName() {
            return name;
        }
    
        /**
         * @param name
         *            the name to set
         */
        public void setName(String name) {
            this.name = name;
        }
    
        /**
         * @return the address
         */
        public String getAddress() {
            return address;
        }
    
        /**
         * @param address
         *            the address to set
         */
        public void setAddress(String address) {
            this.address = address;
        }
    
        /**
         * @return the contactNumber
         */
        public String getContactNumber() {
            return contactNumber;
        }
    
        /**
         * @param contactNumber
         *            the contactNumber to set
         */
        public void setContactNumber(String contactNumber) {
            this.contactNumber = contactNumber;
        }
    
        /**
         * @return the acctList
         */
        public List<BankAccount> getAcctList() {
            return acctList;
        }
    
        /**
         * @param acctList
         *            the acctList to set
         */
        public void setAcctList(List<BankAccount> acctList) {
            this.acctList = acctList;
        }

}


IBankOperations.java
Interface for banking operations

package com.kunaal.service;

import com.kunaal.model.BankAccount;

/**
 * @author ktrehan
 * 
 */
public interface IBankOperations {

    /**
     * Method for depositing money in a particular account
     * 
     * @param acctNumber
     * @param amount
     */
    void deposit(String acctNumber, Double amount);

    /**
     * Method for withdrawing money from a particular account
     * 
     * @param acctNumber
     * @param amount
     */
    void withdrawl(String acctNumber, Double amount);

    /**
     * Method for fetching account details
     * 
     * @param acctNumber
     * @return
     */
    BankAccount acctDetails(String acctNumber);

    /**
     * Method for updating the address
     * 
     * @param acctNumber
     * @param newAddress
     */
    void updateAddress(String acctNumber, String newAddress);

    /**
     * Method for updating contact number
     * 
     * @param acctNumber
     * @param contactNumber
     */
    void updateContactNum(String acctNumber, String contactNumber);
}


BankOperationsImpl.java
Implementation of IBankOperations interface

package com.kunaal.service;

import java.util.List;

import com.kunaal.model.Bank;
import com.kunaal.model.BankAccount;

/**
 * Implementation for IBankOperations interface
 * 
 * @author ktrehan
 */
public class BankOperationsImpl implements IBankOperations {

    // Variable for Bank
    private Bank bank;

    /**
     * Parameterized constructor
     * 
     * @param bank
     */
    public BankOperationsImpl(Bank bank) {
        this.bank = bank;
    }

    /**
     * Method for depositing money in a particular account
     * 
     * @param acctNumber
     * @param amount
     */
    public void deposit(String acctNumber, Double amount) {
        List<BankAccount> acctList = bank.getAcctList();
        boolean acctMatched = false;

        for (BankAccount bankAcct : acctList) {
            String acctVal = bankAcct.getAcctNumber();

            if (acctVal != null && acctVal.equals(acctNumber)) {
                Double currBalance = bankAcct.getCurrBalance();
                bankAcct.setCurrBalance(currBalance + amount);

                acctMatched = true;
                break;
            }

        }

        if (!acctMatched)
            throw new RuntimeException("No acct matched");
    }

    /**
     * Method for withdrawing money from a particular account
     * 
     * @param acctNumber
     * @param amount
     */
    public void withdrawl(String acctNumber, Double amount) {
        List<BankAccount> acctList = bank.getAcctList();
        boolean acctMatched = false;

        for (BankAccount bankAcct : acctList) {
            String acctVal = bankAcct.getAcctNumber();

            if (acctVal != null && acctVal.equals(acctNumber)) {
                Double currBalance = bankAcct.getCurrBalance();
                bankAcct.setCurrBalance(currBalance - amount);

                acctMatched = true;
                break;
            }
        }

        if (!acctMatched)
            throw new RuntimeException("No acct matched");

    }

    /**
     * Method for fetching account details
     * 
     * @param acctId
     * @return
     */
    public BankAccount acctDetails(String acctNumber) {
        List<BankAccount> acctList = bank.getAcctList();
        boolean acctMatched = false;
        BankAccount returnVal = null;

        for (BankAccount bankAcct : acctList) {
            String acctVal = bankAcct.getAcctNumber();

            if (acctVal != null && acctVal.equals(acctNumber)) {
                acctMatched = true;
                returnVal = bankAcct;
                break;
            }
        }

        if (!acctMatched)
            throw new RuntimeException("No acct matched");

        return returnVal;
    }

    /**
     * Method for updating the address
     * 
     * @param acctId
     * @param newAddress
     */
    public void updateAddress(String acctNumber, String newAddress) {
        List<BankAccount> acctList = bank.getAcctList();
        boolean acctMatched = false;

        for (BankAccount bankAcct : acctList) {
            String acctVal = bankAcct.getAcctNumber();

            if (acctVal != null && acctVal.equals(acctNumber)) {
                acctMatched = true;
                bankAcct.setAddress(newAddress);
                break;
            }
        }

        if (!acctMatched)
            throw new RuntimeException("No acct matched");

    }

    /**
     * Method for updating contact number
     * 
     * @param acctId
     * @param contactNumber
     */
    public void updateContactNum(String acctNumber, String contactNumber) {
        List<BankAccount> acctList = bank.getAcctList();
        boolean acctMatched = false;

        for (BankAccount bankAcct : acctList) {
            String acctVal = bankAcct.getAcctNumber();

            if (acctVal != null && acctVal.equals(acctNumber)) {
                bankAcct.setContactNumber(contactNumber);
                acctMatched = true;
                break;
            }
        }

        if (!acctMatched)
            throw new RuntimeException("No acct matched");

    }

    /**
     * @return the bank
     */
    public Bank getBank() {
        return bank;
    }

    /**
     * @param bank
     *                the bank to set
     */
    public void setBank(Bank bank) {
        this.bank = bank;
    }
}


Test Classes
Now lets test the methods for BankOperationsImpl.

Test case showing usage of 
  • @Test
  • @BeforeClass
  • @AfterClass
package com.kunaal.service;

import static org.junit.Assert.assertEquals;

import java.util.List;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import com.kunaal.model.Bank;
import com.kunaal.model.BankAccount;

/**
 * Test class showing usage of @BeforeClass ,@AfterClass Besides this we are
 * testing implementations of IBankOperations methods.
 * 
 * @author ktrehan
 */
public class BankOpTest {

    // Static variable for Bank
    private static Bank bank;

    // Static variable for IBankOperations
    private static IBankOperations bankOperations;

    /**
     * Static method for constructing different bank accounts and wiring the
     * same with the bank
     */
    @BeforeClass
    public static void setup() {
        System.out.println("Static setup invoked");

        BankAccount bankAcct1 = new BankAccount("AcctNum-1", "Mr. A1",
                "Addr-1", "Contact-1", new Double("10000"));
        BankAccount bankAcct2 = new BankAccount("AcctNum-2", "Mr. A2",
                "Addr-2", "Contact-2", new Double("20000"));
        BankAccount bankAcct3 = new BankAccount("AcctNum-3", "Mr. A3",
                "Addr-3", "Contact-3", new Double("30000"));

        bank = new Bank("ICICI", "Bangalore", "91-80-4189000");
        List<BankAccount> acctList = bank.getAcctList();
        acctList.add(bankAcct1);
        acctList.add(bankAcct2);
        acctList.add(bankAcct3);

        bankOperations = new BankOperationsImpl(bank);
    }

    /**
     * Static method for tear down
     */
    @AfterClass
    public static void teardown() {
        System.out.println("Static teardown invoked");
    }

    /**
     * Test case for deposit operation
     */
    @Test
    public void deposit() {
        System.out.println("1. DEPOSIT TEST CASE INVOKED");
        bankOperations.deposit("AcctNum-1", new Double("123"));
        BankAccount acctDetails = bankOperations
                .acctDetails("AcctNum-1");

        assertEquals(acctDetails.getCurrBalance(), new Double("10123"));
    }

    /**
     * Test case for withdraw operation
     */
    @Test
    public void withdraw() {
        System.out.println("2. WITHDRAW TEST CASE INVOKED");
        bankOperations.withdrawl("AcctNum-2", new Double("100"));
        BankAccount acctDetails = bankOperations
                .acctDetails("AcctNum-2");

        assertEquals(acctDetails.getCurrBalance(), new Double("19900"));
    }

    /**
     * Test case for updating address
     */
    @Test
    public void updateAddr() {
        System.out.println("3. UPDATE ADDRESS TEST CASE INVOKED");
        bankOperations.updateAddress("AcctNum-3", "Delhi");
        BankAccount acctDetails = bankOperations
                .acctDetails("AcctNum-3");

        assertEquals(acctDetails.getAddress(), "Delhi");
    }

    /**
     * Test case for updating phone
     */
    @Test
    public void updatePhone() {
        System.out.println("4. UPDATE PHONE TEST CASE INVOKED");
        bankOperations.updateContactNum("AcctNum-3", "NewContactNumber");
        BankAccount acctDetails = bankOperations
                .acctDetails("AcctNum-3");

        assertEquals(acctDetails.getContactNumber(), "NewContactNumber");
    }
}


Output of running the test case
It shows that @BeforeClass and @AfterClass is executed once for TestClass.
Static setup invoked
1. DEPOSIT TEST CASE INVOKED
2. WITHDRAW TEST CASE INVOKED
3. UPDATE ADDRESS TEST CASE INVOKED
4. UPDATE PHONE TEST CASE INVOKED
Static teardown invoked


Test case showing usage of
  • @Before
  • @After
  • timeout parameter in @Test
  • exception parameter in @Test

package com.kunaal.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.util.List;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.kunaal.model.Bank;
import com.kunaal.model.BankAccount;

/**
 * Test case showing the usage of @After @Before along with timeout and
 * exception check
 * 
 * @author ktrehan
 */
public class ScenarioTest {

    // Variable for bank
    private Bank bank;

    // Variable for IBankOperations
    private IBankOperations bankOp;

    /**
     * Non static method for creating different bank accounts and wiring the
     * same.
     */
    @Before
    public void init() {
        System.out.println("Non Static setup invoked");

        BankAccount bankAcct1 = new BankAccount("AcctNum-1", "Mr. A1",
                "Addr-1", "Contact-1", new Double("10000"));
        BankAccount bankAcct2 = new BankAccount("AcctNum-2", "Mr. A2",
                "Addr-2", "Contact-2", new Double("20000"));
        BankAccount bankAcct3 = new BankAccount("AcctNum-3", "Mr. A3",
                "Addr-3", "Contact-3", new Double("30000"));

        bank = new Bank("ICICI", "Bangalore", "91-80-4189000");
        List<BankAccount> acctList = bank.getAcctList();
        acctList.add(bankAcct1);
        acctList.add(bankAcct2);
        acctList.add(bankAcct3);

        bankOp = new BankOperationsImpl(bank);
    }

    /**
     * Non static method for tear down
     */
    @After
    public void tearDown() {
        bankOp = null;
        bank = null;
        System.out.println("        Non static tear down invoked");
    }

    /**
     * Test case for getting account details
     */
    @Test
    public void getAcctDetails() {
        System.out.println("1. Acct details for AcctNum-1 invoked");
        BankAccount acctDetails = bankOp.acctDetails("AcctNum-1");
        assertEquals(acctDetails.getCurrBalance(), new Double("10000"));
    }

    /**
     * Test case for getting account details
     */
    @Test
    public void getAcctDetails1() {
        System.out.println("2. Acct details for AcctNum-2 invoked");
        BankAccount acctDetails = bankOp.acctDetails("AcctNum-2");
        assertEquals(acctDetails.getCurrBalance(), new Double("20000"));
    }

    /**
     * Test case for timeout Here we have given timeout of 2000ms and in the
     * body we have given sleep of 3000ms So testcase will fail
     */
    @Test(timeout = 2000)
    public void timeOutTest() {
        System.out.println("3. Test case for timeout invoked");
        try {
            Thread.currentThread().sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        BankAccount acctDetails = bankOp.acctDetails("AcctNum-2");
        assertEquals(acctDetails.getCurrBalance(), new Double("20000"));
    }

    /**
     * Test case for exception verification
     */
    @Test(expected = RuntimeException.class)
    public void exceptionTest() {
        System.out.println("4. Test case for exception handling");
        BankAccount acctDetails = bankOp.acctDetails("123");
        fail();
    }
}



Output of running this test case.
It shows that @Before and @After is invoked for every test case.
Besides this timeout exception happened and test case with timeout failed. 

Non Static setup invoked
1. Acct details for AcctNum-1 invoked
        Non static tear down invoked
Non Static setup invoked
2. Acct details for AcctNum-2 invoked
        Non static tear down invoked
Non Static setup invoked
3. Test case for timeout invoked
        Non static tear down invoked
java.lang.Exception: test timed out after 2000 milliseconds
    at java.lang.Thread.sleep(Native Method)
    at com.kunaal.service.ScenarioTest.timeOutTest(ScenarioTest.java:91)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.FailOnTimeout$1.run(FailOnTimeout.java:28)

Non Static setup invoked
4. Test case for exception handling
        Non static tear down invoked


TestCase showing usage of
  • @Parameters
  • @RunWith(value = Parameterized.class)
There are scenarios when you want to test the implementation for various input values.
So if you've ever found yourself writing a series of tests which differ only in their inputs and expected results, you've probably realized that the sensible thing to do would be to abstract your tests into a single test that can be run against a varying set of data. JUnit 4 allows you to do this with either theories or parameterized tests

Structure of a parameterized test class

To mark a test class as a parameterized test, you must first annotate it with @RunWith(Parameterized.class). The class must then provide at least three entities:

  • Static method that generates and returns test data,
  • Single constructor that stores the test data
  • Test

The method that generates test data must be annotated with @Parameters, and it must return a Collection of Arrays. Each array represents the data to be used in a particular test run. The number of elements in each array must correspond to the number of parameters in the class's constructor, because each array element will be passed to the constructor, one at a time as the class is instantiated over and over.

The constructor is simply expected to store each data set in the class's fields, where they can be accessed by the test methods. Note that only a single constructor may be provided. This means that each array provided by the data-generating method must be the same size, and you might have to pad your data sets with nulls if you don't always need a particular value.

Let's put this together. When the test runner is invoked, the data-generating method will be executed, and it will return a Collection of Arrays, where each array is a set of test data. The test runner will then instantiate the class and pass the first set of test data to the constructor. The constructor will store the data in its fields. Then each test method will be executed, and each test method will have access to that first set of test data. After each test method has executed, the object will be instantiated again, this time using the second element in the Collection of Arrays, and so on.

Use case of CurrencyConverter
CurrencyConverter converts amount into INR.It has one public method valInINR(...,..) for the same.


CurrencyConverter.java
This class converts the amount into INR.

package com.kunaal.converter;

import java.util.HashMap;
import java.util.Map;

/**
 * Java class for doing currency conversion
 * 
 * @author ktrehan
 */
public class CurrencyConverter {

    /**
     * Map for holding currency and conversion factor
     */
    private Map<String, Double> conversionMap = new HashMap<String, Double>();

    /**
     * Constructor for currency conversion Here we map currency code and
     * conversion amount for INR
     */
    public CurrencyConverter() {
        conversionMap.put("USD", new Double(53));
        conversionMap.put("UK", new Double(65));
        conversionMap.put("AUD", new Double(35));
        conversionMap.put("CAD", new Double(45));
    }

    /**
     * Method to get value in INR for a particular amount in different
     * currency
     * 
     * @param currCode
     * @param amount
     * @return
     */
    public Double valInINR(String currCode, Double amount) {
        return conversionMap.get(currCode) * amount;
    }

}


Lets write the parameterized test case where we convert 1000 USD/UK/AUD/CAD into Indian Rupees.We will create the test input in one static block,which will be used by the constructor and test method is invoked.

Test case showing usage of
  • @Parameters

package com.kunaal.service;

import static org.junit.Assert.assertEquals;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

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

import com.kunaal.converter.CurrencyConverter;

/**
 * @author ktrehan
 * 
 */
@RunWith(value = Parameterized.class)
public class ParameterizedTest {

    // Input parameter for currency code
    private String currCode;

    // Input parameter for amount
    private Double inputAmt;

    // Output parameter for converted amount
    private Double outAmt;

    // Static variable for Currency Converter
    private static CurrencyConverter currConverter;

    /**
     * Static method for test input and output
     * 
     * @return
     */
    @Parameters
    public static Collection<Object[]> paramData() {
        List<Object[]> dataList = new ArrayList<Object[]>();
        dataList.add(new Object[] { "USD", new Double(1000),
                new Double(53000) });
        dataList.add(new Object[] { "UK", new Double(1000),
                new Double(65000) });
        dataList.add(new Object[] { "AUD", new Double(1000),
                new Double(35000) });
        dataList.add(new Object[] { "CAD", new Double(1000),
                new Double(45000) });

        return dataList;
    }

    /**
     * Constructor taking input parameters and corresponding output
     * 
     * @param currCode
     * @param inptAmt
     * @param outAmt
     */
    public ParameterizedTest(String currCode, Double inptAmt, Double outAmt) {
        this.currCode = currCode;
        this.inputAmt = inptAmt;
        this.outAmt = outAmt;
    }

    /**
     * Static @BeforeClass method
     */
    @BeforeClass
    public static void setup() {
        currConverter = new CurrencyConverter();
    }

    /**
     * Test Case for checking conversion amount
     */
    @Test
    public void valInINR() {
        System.out.println("Currency code used -" + this.currCode
    + " ,amount for conversion-" + this.inputAmt);
 assertEquals(currConverter.valInINR(this.currCode,
   this.inputAmt), this.outAmt);
 System.out.println("Output amount-" + this.outAmt);
    }
}


Output of running this test case is as follows:-
Currency code used -USD ,amount for conversion-1000.0
Output amount-53000.0
Currency code used -UK ,amount for conversion-1000.0
Output amount-65000.0
Currency code used -AUD ,amount for conversion-1000.0
Output amount-35000.0
Currency code used -CAD ,amount for conversion-1000.0
Output amount-45000.0


So this completes some of the important features of JUnit4.




Obstacles in the path of Unit testing

Most of classes in production code has dependencies on other classes or modules.
When writing a unit test one of the challenges is how to code around the dependencies of the object of his test. In other words – how to isolate the code under test from external dependencies.

Imagine a use case work where we need to interact with a web service and apply some computation logic on the response received.

Sometimes in web application we fetch data from HTTPSession and work on it.So how we should get HTTPSession data during unit test phase.

Sometimes method under test needs access to database.So we should provide database access.

So to resolve all such issues, we have fake objects like Mocks and Stubs.



Mocks and Stubs

In order  to get rid of dependent objects during unit testing.We need fake objects.Mocks and Stubs are two ways to achieve the same.

Stubs
Stubs provide pre -programmed answers to method invocation.Testing methodology followed by Stubs is 'State Based'

Mocks
Mocks mimics the real objects in controlled defined way.Testing methodology followed by Mocks is 'Interaction Based'.







In order to understand the difference between Stub and Mock ,lets take the use case of NetWorthCalculator.

Use case has following classes.These are:-
  • IStockFeeder-This interface contains method for finding net stock value based on demat account number
  • ILoanFeeder-This interface contains method to find principal amount pending.
  • IMFFeeder-This interface contains method for finding net value based on mutual fund account number.
  • NetWorthCalculator-Implementation class having MFFeeder,LoanFeeder and StockFeeder as attributes.netWorth() method calculates net worth based on some rules.

ILoanFeeder.java

package com.kunaal.feeder;

/**
 * Interface for Loan information
 * 
 * @author ktrehan
 */
public interface ILoanFeeder {

    /**
     * Method for finding pending principal amount based on laon account
     * number.
     * 
     * @param loanActNum
     * @return
     */
    Double pendingPrinAmount(String loanActNum);
}


IMFFeeder.java

package com.kunaal.feeder;

/**
 * Interface for finding Mutual Fund information
 * 
 * @author ktrehan
 */
public interface IMFFeeder {

    /**
     * Method for finding net Mutual fund value based on account number
     * 
     * @param mfAcctNum
     * @return
     */
    Double netMFValue(String mfAcctNum);

}


IStockFeeder.java

package com.kunaal.feeder;

/**
 * Interface for finding stock information.
 * 
 * @author ktrehan
 */
public interface IStockFeeder {

    /**
     * Method for finding net stock value based on demat account number.
     * 
     * @param dematAcctNum
     * @return
     */
    Double netStockValue(String dematAcctNum);
}


NetWorthCalculator.java

package com.kunaal;

import com.kunaal.feeder.ILoanFeeder;
import com.kunaal.feeder.IMFFeeder;
import com.kunaal.feeder.IStockFeeder;

/**
 * Networth calculator class.
 * 
 * @author ktrehan
 */
public class NetWorthCalculator {

    // Variable for IMFFeeder
    private IMFFeeder mfFeeder;

    // Variable for IStockFeeder
    private IStockFeeder stockFeeder;

    // Variable for ILoanFeeder
    private ILoanFeeder loanFeeder;

    // Variable for cash amount
    private Double cashAmt;

    // Variable for fixed asset val
    private Double fixedAssetVal;

    // Boolean variable stating whether any loans are there or not
    private boolean anyLoans;

    // Variable for Mutual Fund Account Number
    private String mfAcctNum;

    // Variable for Demat Account Number
    private String dematAcctNum;

    // Variable for Loan Account Number
    private String loanAcctNum;

    /**
     * Parameterized constructor
     * 
     * @param mfFeeder
     * @param stockFeeder
     * @param loanFeeder
     * @param cashAmt
     * @param fixedAssetVal
     * @param anyLoans
     * @param mfAcctNum
     * @param dematAcctNum
     * @param loanAcctNum
     */
    public NetWorthCalculator(IMFFeeder mfFeeder, IStockFeeder stockFeeder,
            ILoanFeeder loanFeeder, Double cashAmt,
            Double fixedAssetVal, boolean anyLoans,
            String mfAcctNum, String dematAcctNum,
            String loanAcctNum) {
        this.mfFeeder = mfFeeder;
        this.stockFeeder = stockFeeder;
        this.loanFeeder = loanFeeder;
        this.cashAmt = cashAmt;
        this.fixedAssetVal = fixedAssetVal;
        this.anyLoans = anyLoans;
        this.mfAcctNum = mfAcctNum;
        this.dematAcctNum = dematAcctNum;
        this.loanAcctNum = loanAcctNum;
    }

    /**
     * Method having logic for finding total worth
     * 
     * @return
     */
    public Double netWorth() {
        Double netWorthVal = new Double(0);

        netWorthVal = cashAmt + 0.85 * fixedAssetVal + 0.9
                * mfFeeder.netMFValue(mfAcctNum) + 0.7
                * stockFeeder.netStockValue(dematAcctNum);

        if (anyLoans) {
            netWorthVal = netWorthVal
                    - (1.3 * loanFeeder
                            .pendingPrinAmount(loanAcctNum));
        }

        return netWorthVal;
    }

}


Testing NetWorthCalculator using Stub Methodology

Here we have write following stubs with hard coded  return values. These are:-
  • StubMFFeeder
  • StubLoanFeeder
  • StubStockFeeder

    These stubs are then wired in NetWorthCalculatorTest.While creating object of NetWorthCalculator,we are passing false to 'anyLoans'.So even though we have created StubLoanFeeder,method for finding pending principal amount is never invoked.However with the use of stub methodology this can't be verified.
StubMFFeeder.java
 
package com.kunaal.stub;

import com.kunaal.feeder.IMFFeeder;

/**
 * Stub for MF feeder
 * 
 * @author ktrehan
 */
public class StubMFFeeder implements IMFFeeder {

    /**
     * Hard coded value for method implementation
     */
    public Double netMFValue(String mfAcctNum) {
        return new Double(100000);
    }

}


StubLoanFeeder.java
 
package com.kunaal.stub;

import com.kunaal.feeder.ILoanFeeder;

/**
 * Stub for Loan Feeder
 * 
 * @author ktrehan
 */
public class StubLoanFeeder implements ILoanFeeder {

    /**
     * Hard coded value for method implementation
     */
    public Double pendingPrinAmount(String loanActNum) {
        return new Double(50000);
    }

}


StubStockFeeder.java

package com.kunaal.stub;

import com.kunaal.feeder.IStockFeeder;

/**
 * Stub implementation for Stock Feeder
 * 
 * @author ktrehan
 */
public class StubStockFeeder implements IStockFeeder {

    /**
     * Hard coded value for method implementation.
     */
    public Double netStockValue(String dematAcctNum) {
        return new Double(20000);
    }

}


NetWorthCalcTest.java
 
package com.kunaal.stub;

import static org.junit.Assert.assertEquals;

import org.junit.BeforeClass;
import org.junit.Test;

import com.kunaal.NetWorthCalculator;
import com.kunaal.feeder.ILoanFeeder;
import com.kunaal.feeder.IMFFeeder;
import com.kunaal.feeder.IStockFeeder;

/**
 * Stub approach for testing Net Worth Calculator.
 * 
 * @author ktrehan
 */
public class NetWorthCalcTest {

    // Variable for IMFFeeder
    private static IMFFeeder mfFeeder;

    // Variable for IStockFeeder
    private static IStockFeeder stockFeeder;

    // Variable for ILoanFeeder
    private static ILoanFeeder loanFeeder;

    // Static variable for NetWorthCalculator
    private static NetWorthCalculator netWorthCalc;

    /**
     * Static method for object creation.
     */
    @BeforeClass
    public static void setup() {
        mfFeeder = new StubMFFeeder();
        stockFeeder = new StubStockFeeder();
        loanFeeder = new StubLoanFeeder();

        netWorthCalc = new NetWorthCalculator(mfFeeder, stockFeeder,
                loanFeeder, new Double(25000), new Double(
                        1000000), false, "MF-1",
                "Demat-1", null);
    }

    /**
     * Test method for finding networth.
     */
    @Test
    public void networth() {
        assertEquals(netWorthCalc.netWorth(), new Double(979000));
        System.out.println("Test case passed");
    }
}


Output
Test cases will run successfully with console output of 'Test case passed'.

Testing NetWorthCalculator using Mock Methodology

Here we are creating only one class and using Mockito library for creating mocks of dependencies.

NetWorthCalcTest.java

package com.kunaal.mock;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.BeforeClass;
import org.junit.Test;

import com.kunaal.NetWorthCalculator;
import com.kunaal.feeder.ILoanFeeder;
import com.kunaal.feeder.IMFFeeder;
import com.kunaal.feeder.IStockFeeder;

/**
 * Mock approach for testing NetWorthCalculator
 * 
 * @author ktrehan
 */
public class NetWorthCalcTest {

    // Variable for IMFFeeder
    private static IMFFeeder mfFeeder;

    // Variable for IStockFeeder
    private static IStockFeeder stockFeeder;

    // Variable for ILoanFeeder
    private static ILoanFeeder loanFeeder;

    // Variable for NetWorthCalculator
    private static NetWorthCalculator netWorthCalc;

    /**
     * Static method for creation of mocks and wiring the same
     */
    @BeforeClass
    public static void setup() {
        // Creating mock objects of IMFFeeder,IStockFeeder,ILoanFeeder
        mfFeeder = mock(IMFFeeder.class);
        stockFeeder = mock(IStockFeeder.class);
        loanFeeder = mock(ILoanFeeder.class);

        netWorthCalc = new NetWorthCalculator(mfFeeder, stockFeeder,
                loanFeeder, new Double(25000), new Double(
                        1000000), false, "MF-1",
                "Demat-1", null);
    }

    /**
     * Testing the netWorth()
     */
    @Test
    public void networth() {
        // stubbing or creating the expectations
        when(mfFeeder.netMFValue(anyString())).thenReturn(
                new Double(100000));
        when(stockFeeder.netStockValue(anyString())).thenReturn(
                new Double(20000));
        when(loanFeeder.pendingPrinAmount(anyString())).thenReturn(
                new Double(50000));

        // Testing the netWorth() method implementation
        assertEquals(netWorthCalc.netWorth(), new Double(979000));

        // verification routines for method invocation
        verify(mfFeeder, times(1)).netMFValue(anyString());
        verify(stockFeeder, times(1)).netStockValue(anyString());
        verify(loanFeeder, times(1)).pendingPrinAmount(anyString());
    }
}


Output

Wanted but not invoked:
iLoanFeeder.pendingPrinAmount(<any>);
-> at com.kunaal.mock.NetWorthCalcTest.networth(NetWorthCalcTest.java:72)
Actually, there were zero interactions with this mock.

    at com.kunaal.mock.NetWorthCalcTest.networth(NetWorthCalcTest.java:72)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)



So you can see using mock approach has following advantages over stub approach.These are:-
  • We donot need to hand code the stub classes
  • Creation of mock is easy.
  • Method expectations can be set easily
  • Besides state ,we can also verify the interactions.
In the above test case,ILoanFeeder.pendingPrinAmount(...) is never invoked. So test case fails at method verification stage.



Mockito Features

  1. Mockito can mock both interface and class.
    Mockito can mock both interfaces and class.It uses proxy to achieve this.
    In this example,we will mock List(An interface) and Exception (A class)

    ListTest.java
    package com.kunaal.intClass;
    
    import static org.junit.Assert.assertEquals;
    import static org.mockito.Matchers.anyString;
    import static org.mockito.Mockito.mock;
    import static org.mockito.Mockito.times;
    import static org.mockito.Mockito.verify;
    import static org.mockito.Mockito.when;
    
    import java.util.List;
    
    import org.junit.Test;
    
    /**
     * This test class illustrates that mockito can mock both class and interfaces
     * It uses both java dynamic proxy and cglib and asm handlers
     * 
     * @author ktrehan
     */
    public class ListTest {
    
        /**
         * Mockito in picture for handling interfaces
         */
        @Test
        public void interfaceTest() {
            // Mock object creation
            List<String> mockList = mock(List.class);
    
            // method invocation on mocked object
            mockList.add("USA");
            mockList.add("UK");
            mockList.add("India");
    
            // Verifying the interaction
            verify(mockList, times(3)).add(anyString());
        }
    
        /**
         * Mockito in picture for handling classes
         */
        @Test
        public void classTest() {
            // Mock object creation
            Exception mockException = mock(Exception.class);
            String output = "Test exception message";
    
            // Setting expectation
            when(mockException.getMessage()).thenReturn(output);
    
            assertEquals(output, mockException.getMessage());
    
            // verify the interaction
            verify(mockException, times(1)).getMessage();
        }
    }
    
    
  2. Easy way of matching method argument.

    A method invocation can happen for any value.Mockito has a feature to generalize this.Instead of hard coding we can write any...() tags for argument matching.
    Here we have a TaxCalculator which provides tax percentage depending upon the country and
    salary amount

    TaxCalculator.java
    package com.kunaal.matchers;
    
    /**
     * Utility class for finding tax percentage
     * 
     * @author ktrehan
     */
    public class TaxCalc {
    
        /**
         * Utility method for finding tax percentage slab
         * 
         * @param country
         * @param annIncome
         * @return
         */
        public double getTaxBracket(String country, Double annIncome) {
            double taxPercentage = 0d;
    
            if (country.equalsIgnoreCase("USA")) {
                if (annIncome < 1000000)
                    taxPercentage = 10.5;
                else
                    taxPercentage = 15;
            } else if (country.equalsIgnoreCase("UK")) {
                if (annIncome < 50000)
                    taxPercentage = 12;
                else
                    taxPercentage = 19;
            } else {
                taxPercentage = 13;
            }
    
            return taxPercentage;
        }
    }
    
    

    Test class showing usage of argument matchers(anyString(),anyDouble(),....)

    MatcherTest.java

    package com.kunaal.matchers;
    
    import static org.junit.Assert.assertEquals;
    import static org.mockito.Matchers.anyDouble;
    import static org.mockito.Matchers.anyString;
    import static org.mockito.Mockito.mock;
    import static org.mockito.Mockito.times;
    import static org.mockito.Mockito.verify;
    import static org.mockito.Mockito.when;
    
    import org.junit.Test;
    
    /**
     * This test class illustrates the argument matcher feature present in Mockito
     * 
     * @author ktrehan
     */
    public class MatcherTest {
    
        /**
         * Mockito feature showing argument matcher like
         * anyString(),anyDouble().....
         */
        @Test
        public void multiArgTest() {
            TaxCalc mockObj = mock(TaxCalc.class);
    
            // Setting the expectation
            when(mockObj.getTaxBracket(anyString(), anyDouble()))
                    .thenReturn(24d);
            double taxBracket = mockObj.getTaxBracket("USA", 12345678d);
            // assertion
            assertEquals(taxBracket, 24d, 0.0d);
            // verifications
            verify(mockObj, times(1)).getTaxBracket(anyString(),
                    anyDouble());
        }
    
        /**
         * Mockito feature showing argument matcher like
         * anyString(),anyDouble()..... along with different results for
         * multiple invocations.
         */
        @Test
        public void multiArgTest1() {
            TaxCalc mockObj = mock(TaxCalc.class);
    
            // Setting the expectation
            when(mockObj.getTaxBracket(anyString(), anyDouble()))
                    .thenReturn(24d).thenReturn(50d);
    
            double taxBracket = mockObj.getTaxBracket("USA", 12345678d);
            // assertion
            assertEquals(taxBracket, 24d, 0.0d);
    
            taxBracket = mockObj.getTaxBracket("UK", 123334455d);
            // assertion
            assertEquals(taxBracket, 50d, 0.0d);
    
            // verification
            verify(mockObj, times(2)).getTaxBracket(anyString(),
                    anyDouble());
        }
    
    }
    
    
  3. Verify number of method invocations and invocation order.

    Mockito has a feature for verifying how many times a particular method is invoked
    and in which order method invocations on particular object has happened.

    InvocationTest.java

    package com.kunaal.verify;
    
    import static org.mockito.Matchers.anyString;
    import static org.mockito.Mockito.inOrder;
    import static org.mockito.Mockito.mock;
    import static org.mockito.Mockito.never;
    import static org.mockito.Mockito.times;
    import static org.mockito.Mockito.verify;
    
    import java.util.List;
    
    import org.junit.Test;
    import org.mockito.InOrder;
    
    /**
     * This test class illustrates the usage of verify and verification order
     * 
     * @author ktrehan
     */
    public class InvocationTest {
    
        /**
         * Mockito illustrating number of times a particular method is invoked
         */
        @Test
        public void addOperation() {
            // Mock
            List<String> mockList = mock(List.class);
            mockList.add("A");
            mockList.add("B");
            mockList.add("C");
            mockList.add("D");
    
            // Verifying number of invocations for add(..)
            verify(mockList, times(4)).add(anyString());
    
            // Verifying number of invocations for clear(...)
            verify(mockList, never()).clear();
        }
    
        /**
         * Mockito in picture showing order in which methods are executed
         */
        @Test
        public void invocationOrder() {
            // Mock
            List<String> mockList = mock(List.class);
            InOrder inorder = inOrder(mockList);
    
            mockList.add("A");
            mockList.add("B");
            mockList.get(0);
            mockList.clear();
    
            // Invocation order verification
            inorder.verify(mockList).clear();
            inorder.verify(mockList).add("A");
            inorder.verify(mockList).add("B");
            inorder.verify(mockList).get(0);
            inorder.verify(mockList).clear();
        }
    }
    
    

    Here addOperation() shows how many times add() method is invoked and invocationOrder() shows the order in which methods are invoked.

    Output

    org.mockito.exceptions.verification.VerificationInOrderFailure: 
    Verification in order failure
    Wanted but not invoked:
    list.add("A");
    -> at com.kunaal.verify.InvocationTest.invocationOrder(InvocationTest.java:55)
    Wanted anywhere AFTER following interaction:
    list.clear();
    -> at com.kunaal.verify.InvocationTest.invocationOrder(InvocationTest.java:52)
    
        at com.kunaal.verify.InvocationTest.invocationOrder(InvocationTest.java:55)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
        at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
        at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
    
    
    

  4. Handling of generics Mockito handle Generics in different ways.One is by casting and another is by using annotations provided by Mockito.




    To understand it better ,lets take above use case where Globe class contains a map containing map for currency and country details.We have following two generic variables.These are:-
    -private Map<String, String> currMap;  [Variable for currency information corresponding to country code]
    -private Map<String, CtryDetails> ctryMap; [Variable for country details corresponding to country code]

    Globe.java

    package com.kunaal.generics;
    
    import java.util.List;
    import java.util.Map;
    
    /**
     * Class  having composition of 
     * -Country Details Map
     * -Currency Map
     * 
     * @author ktrehan
     */
    public class Globe {
    
        // Variable for capturing currency map
        private Map<String, String> currMap;
    
        // Variable for capturing country details
        private Map<String, CtryDetails> ctryMap;
    
        /**
         * Default constructor
         */
        public Globe() {
        }
    
        /**
         * Method for finding country information.
         * 
         * @param countryName
         * @return
         */
        public String ctryType(String countryName) {
            CtryDetails ctryDetails = ctryMap.get(countryName);
            Long population = null;
    
            if (ctryDetails != null) {
                population = ctryDetails.getPopulation();
    
                if (population > 100000000)
                    return "Heavily Populated";
                else
                    return "Not heavily populated";
            } else {
                return "Country Info not present";
            }
        }
    
        /**
         * Method for finding currency of the country.
         * 
         * @param countryName
         * @return
         */
        public String getCurrency(String countryName) {
            String currency = currMap.get(countryName);
    
            if (currency != null)
                return currency;
            else
                return "Currency Information not avaialable";
        }
    
        /**
         * Method for finding language details of the country
         * 
         * @param countryName
         * @return
         */
        public String langType(String countryName) {
            CtryDetails ctryDetails = ctryMap.get(countryName);
            List<String> langList = ctryDetails.getLangList();
    
            if (langList != null) {
                if (langList.size() > 1)
                    return "Multi-Lingual";
                else
                    return "One common language";
            } else {
                return "Language data not found";
            }
        }
    
        /**
         * @return the currMap
         */
        public Map<String, String> getCurrMap() {
            return currMap;
        }
    
        /**
         * @param currMap
         *                the currMap to set
         */
        public void setCurrMap(Map<String, String> currMap) {
            this.currMap = currMap;
        }
    
        /**
         * @return the ctryMap
         */
        public Map<String, CtryDetails> getCtryMap() {
            return ctryMap;
        }
    
        /**
         * @param ctryMap
         *                the ctryMap to set
         */
        public void setCtryMap(Map<String, CtryDetails> ctryMap) {
            this.ctryMap = ctryMap;
        }
    }
    
    

    CtryDetails.java

    package com.kunaal.generics;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Model class for Country Details
     * 
     * @author ktrehan
     */
    public class CtryDetails {
    
        // Variable for poulation
        private Long population = 0l;
    
        // Variable for list of languages
        private List<String> langList = new ArrayList<String>();
    
        /**
         * @return the population
         */
        public Long getPopulation() {
            return population;
        }
    
        /**
         * @param population
         *                the population to set
         */
        public void setPopulation(Long population) {
            this.population = population;
        }
    
        /**
         * @return the langList
         */
        public List<String> getLangList() {
            return langList;
        }
    
        /**
         * @param langList
         *                the langList to set
         */
        public void setLangList(List<String> langList) {
            this.langList = langList;
        }
    
    }
    
    


    First version of test case where we use casting for generics

    GlobeTest.java

    package com.kunaal.generics;
    
    import static org.junit.Assert.assertEquals;
    import static org.mockito.Matchers.anyString;
    import static org.mockito.Mockito.mock;
    import static org.mockito.Mockito.when;
    
    import java.util.Map;
    
    import org.junit.BeforeClass;
    import org.junit.Test;
    
    /**
     * Test case for Globe using casting approach
     * 
     * @author ktrehan
     */
    public class GlobeTest {
    
        // Static variable for Globe
        private static Globe globeVar;
    
        // Static variable for currency map
        private static Map<String, String> currMap;
    
        // Static variable for country map
        private static Map<String, CtryDetails> ctryMap;
    
        /**
         * Static initializer
         */
        @BeforeClass
        public static void init() {
            currMap = (Map<String, String>) mock(Map.class);
            ctryMap = (Map<String, CtryDetails>) mock(Map.class);
            globeVar = new Globe();
            globeVar.setCtryMap(ctryMap);
            globeVar.setCurrMap(currMap);
        }
    
        /**
         * Test case for ctryMap
         */
        @Test
        public void ctryType() {
            CtryDetails ctryDetails = new CtryDetails();
            ctryDetails.setPopulation(100000l);
    
            when(ctryMap.get(anyString())).thenReturn(ctryDetails);
            assertEquals(globeVar.ctryType("ABCD"), "Not heavily populated");
        }
    
        /**
         * Test case for currMap
         */
        @Test
        public void getCurrency() {
            when(currMap.get(anyString())).thenReturn("USD");
            assertEquals(globeVar.getCurrency("ABCD"), "USD");
        }
    }
    
    


    Second version of test case using annotations


    GlobeTest1.java


    package com.kunaal.generics;
    
    import static org.junit.Assert.assertEquals;
    import static org.mockito.Matchers.anyString;
    import static org.mockito.Mockito.when;
    
    import java.util.Map;
    
    import org.junit.BeforeClass;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.runners.MockitoJUnitRunner;
    
    /**
     * Test case for Globe using Mockito annotation approach
     * 
     * @author ktrehan
     */
    @RunWith(MockitoJUnitRunner.class)
    public class GlobeTest1 {
    
        @Mock
        private static Map<String, String> currMap;
    
        @Mock
        private static Map<String, CtryDetails> ctryMap;
    
        @InjectMocks
        private static Globe globeVar;
    
        /**
         * Static initializer
         */
        @BeforeClass
        public static void setup() {
            globeVar = new Globe();
            globeVar.setCtryMap(ctryMap);
            globeVar.setCurrMap(currMap);
        }
    
        /**
         * Test case for ctryType
         */
        @Test
        public void ctryType() {
            CtryDetails ctryDetails = new CtryDetails();
            ctryDetails.setPopulation(100000l);
    
            when(ctryMap.get(anyString())).thenReturn(ctryDetails);
            assertEquals(globeVar.ctryType("ABCD"), "Not heavily populated");
        }
    
        /**
         * Test case for currMap
         */
        @Test
        public void getCurrency() {
            when(currMap.get(anyString())).thenReturn("USD");
            assertEquals(globeVar.getCurrency("ABCD"), "USD");
        }
    }
    
    

    Here we are using @Mock and @InjectMocks annotation to plug the generic dependencies.

  5. How to test parameters passed,usage of ArgumentCaptor
    Mockito comes with a concept of capturing the arguments passed to the mocked calls, using     ArgumentCaptor, which captures argument values for further assertion.Suppose you want to find the details of mocked method input.ArgumentCaptor helps us in that
     




    BookingDetails.java
    Model class for booking details

    package com.kunaal.travel;
    
    /**
     * Model class for capturing booking information
     * 
     * @author ktrehan
     */
    public class BookingDetails {
    
        /**
         * Variable for passenger name
         */
        private String passengerName;
        
        /**
         * Variable for boarding point
         */
        private String boarding;
        
        /**
         * Variable for destination point
         */
        private String destination;
    
        /**
         * Variable for gender
         */
        private String gender;
        
        /**
         * Variable for distance
         */
        private Long distance;
    
        /**
         * @return the distance
         */
        public Long getDistance() {
            return distance;
        }
    
        /**
         * @param distance the distance to set
         */
        public void setDistance(Long distance) {
            this.distance = distance;
        }
    
        /**
         * @return the passengerName
         */
        public String getPassengerName() {
            return passengerName;
        }
    
        /**
         * @param passengerName the passengerName to set
         */
        public void setPassengerName(String passengerName) {
            this.passengerName = passengerName;
        }
    
        /**
         * @return the boarding
         */
        public String getBoarding() {
            return boarding;
        }
    
        /**
         * @param boarding the boarding to set
         */
        public void setBoarding(String boarding) {
            this.boarding = boarding;
        }
    
        /**
         * @return the destination
         */
        public String getDestination() {
            return destination;
        }
    
        /**
         * @param destination the destination to set
         */
        public void setDestination(String destination) {
            this.destination = destination;
        }
    
        /**
         * @return the gender
         */
        public String getGender() {
            return gender;
        }
    
        /**
         * @param gender the gender to set
         */
        public void setGender(String gender) {
            this.gender = gender;
        }
        
        
    }
    
    

    TravelCost.java
    Model class for TravelCost



    package com.kunaal.travel;
    
    /**
     * Model class for travel cost 
     * 
     * @author ktrehan
     */
    public class TravelCost {
        
        /**
         * Variable for travel type
         */
        private String travelType;
        
        /**
         * Variable for final fare
         */
        private Double finalFare;
    
        /**
         * @return the travelType
         */
        public String getTravelType() {
            return travelType;
        }
    
        /**
         * @param travelType the travelType to set
         */
        public void setTravelType(String travelType) {
            this.travelType = travelType;
        }
    
        /**
         * @return the finalFare
         */
        public Double getFinalFare() {
            return finalFare;
        }
    
        /**
         * @param finalFare the finalFare to set
         */
        public void setFinalFare(Double finalFare) {
            this.finalFare = finalFare;
        }
    
        /**
         * @param travelType
         * @param finalFare
         */
        public TravelCost(String travelType, Double finalFare) {
            this.travelType = travelType;
            this.finalFare = finalFare;
        }
        
        
    
    }
    
    

    TravelAgent.java
    Class for finding travel cost across different modes

    package com.kunaal.travel;
    
    import java.util.List;
    
    /**
     * Class for calculating fares across different transportation modes
     *  
     * @author ktrehan
     */
    public class TravelAgent {
        
        /**
         * Utility method for booking bus ticket
         * 
         * @param bookingDetails
         * @return
         */
        public Double bookBusTicket(BookingDetails bookingDetails){
            Long distance = bookingDetails.getDistance();
            String gender = bookingDetails.getGender();
            Double deltaFactor=1d;
            
            if(distance >1000 && gender.equals("F")){
                deltaFactor=1.05d;
            }else if(distance <1000 && gender.equals("F")){
                deltaFactor=1.1d;
            }else if(distance >1000 && gender.equals("M")){
                deltaFactor=1.07d;
            }else if(distance <1000 && gender.equals("M")){
                deltaFactor=1.14d;
            }
            
            return deltaFactor* 1200;
        }
        
        /**
         * Utility method for booking train ticket
         * 
         * @param bookingDetails
         * @return
         */
        public Double bookTrainTicket(BookingDetails bookingDetails){
            Long distance = bookingDetails.getDistance();
            String gender = bookingDetails.getGender();
            Double deltaFactor=1d;
            
            if(distance >1000 && gender.equals("F")){
                deltaFactor=1.15d;
            }else if(distance <1000 && gender.equals("F")){
                deltaFactor=1.20d;
            }else if(distance >1000 && gender.equals("M")){
                deltaFactor=1.17d;
            }else if(distance <1000 && gender.equals("M")){
                deltaFactor=1.24d;
            }
            
            return deltaFactor* 2100;
        }
        
        /**
         * Utility method for booking air ticket
         * 
         * @param bookingDetails
         * @return
         */
        public Double bookAirTicket(BookingDetails bookingDetails){
            Long distance = bookingDetails.getDistance();
            String gender = bookingDetails.getGender();
            Double deltaFactor=1d;
            
            if(distance >1000 && gender.equals("F")){
                deltaFactor=1.2d;
            }else if(distance <1000 && gender.equals("F")){
                deltaFactor=1.22d;
            }else if(distance >1000 && gender.equals("M")){
                deltaFactor=1.27d;
            }else if(distance <1000 && gender.equals("M")){
                deltaFactor=1.34d;
            }
            
            return deltaFactor* 3100;
        }
        
        
        public void nullifyDistance(List<BookingDetails> inputList){
            for(BookingDetails data:inputList){
                data.setDistance(0l);
            }
        }
        
        /**
         * Utility method for applying group discount
         * 
         * @param travelCostList
         * @param percentage
         * @return
         */
        public boolean applyGpDiscount(List<TravelCost> travelCostList,Double percentage){
            for(TravelCost cost:travelCostList){
                Double fare = cost.getFinalFare();
                Double discount = fare*percentage/100;
                cost.setFinalFare(fare-discount);
            }
            return true;
        }
    
    }
    
    

    TravelFacade.java
    Facade class which interacts with TravelAgent class and get all the reqd information.

    package com.kunaal.travel;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Facade layer for capturing the information and presenting fares across
     * different transportation modes
     * 
     * @author ktrehan
     */
    public class TravelFacade {
    
        /**
         * Variable for travel agent
         */
        private TravelAgent travelAgent;
    
        /**
         * @return the travelAgent
         */
        public TravelAgent getTravelAgent() {
            return travelAgent;
        }
    
        /**
         * @param travelAgent
         *                the travelAgent to set
         */
        public void setTravelAgent(TravelAgent travelAgent) {
            this.travelAgent = travelAgent;
        }
    
        /**
         * Method for getting travel cost across different travel modes
         * 
         * @param from
         * @param to
         * @param name
         * @param gender
         * @return
         */
        public List<TravelCost> getAllTravelCost(String from, String to,
                String name, String gender) {
            List<TravelCost> travelCostList = new ArrayList<TravelCost>();
            Long distance = 0l;
    
            if (from.equals("Bangalore") && to.equals("Delhi")) {
                distance = 2400l;
            } else if (from.equals("Bangalore") && to.equals("Chennai")) {
                distance = 500l;
            } else {
                distance = 900l;
            }
    
            BookingDetails bookingDetails = new BookingDetails();
            bookingDetails.setBoarding(from);
            bookingDetails.setDestination(to);
            bookingDetails.setDistance(distance);
            bookingDetails.setGender(gender);
            bookingDetails.setPassengerName(name);
    
            Double airFare = travelAgent.bookAirTicket(bookingDetails);
            Double busFare = travelAgent.bookBusTicket(bookingDetails);
            Double trainFare = travelAgent.bookTrainTicket(bookingDetails);
    
            travelCostList.add(new TravelCost("Air", airFare));
            travelCostList.add(new TravelCost("Bus", busFare));
            travelCostList.add(new TravelCost("Train", trainFare));
    
            return travelCostList;
        }
    
    }
    
    


    TravelFacadeTest.java

    Test class for TravelFacade.Here, we have a TravelFacade class which has a method getAllTravelCost(....).Input parameters are from,to,passenger name and gender.While fetching the details, we use TravelAgent class with BookingDetails as input.

    BookingDetails information comes from input parameters along with other business logic .
    Last assertion "assertEquals(new Long(2400), argument.getValue().getDistance());"
    shows the usage of ArgumentCaptor.Here we have a logic to get the distance depending upon start and destination place which we verify by argument.getValue().getDistance().

    package com.kunaal.travel;
    
    import static org.junit.Assert.assertEquals;
    import static org.mockito.Mockito.mock;
    import static org.mockito.Mockito.verify;
    import static org.mockito.Mockito.when;
    
    import java.util.List;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.ArgumentCaptor;
    import org.mockito.runners.MockitoJUnitRunner;
    
    /**
     * Mockito has a very nice feature that allows you to verify what parameters
     * were used when a method was executed.
     * 
     * @author ktrehan
     */
    @RunWith(MockitoJUnitRunner.class)
    public class TravelFacadeTest {
    
        /**
         * This test method shows the usage of ARGUMENT CAPTOR STUFF
         */
        @Test
        public void getAllTravelCost() {
            TravelFacade facade = new TravelFacade();
            TravelAgent mockAgent = mock(TravelAgent.class);
            facade.setTravelAgent(mockAgent);
    
            // Argument Captor
            ArgumentCaptor<BookingDetails> argument = ArgumentCaptor
                    .forClass(BookingDetails.class);
    
            // Input parameters
            String from = "Bangalore";
            String to = "Delhi";
            String name = "Test Passenger";
            String gender = "F";
    
            // Stubbing
            when(mockAgent.bookAirTicket(argument.capture())).thenReturn(
                    1000d);
            when(mockAgent.bookBusTicket(argument.capture())).thenReturn(
                    10d);
            when(mockAgent.bookTrainTicket(argument.capture())).thenReturn(
                    100d);
    
            // Invocation
            List<TravelCost> allTravelCost = facade.getAllTravelCost(from,
                    to, name, gender);
            assertEquals(3, allTravelCost.size());
    
            // Verification
            verify(mockAgent).bookAirTicket(argument.capture());
            verify(mockAgent).bookTrainTicket(argument.capture());
            verify(mockAgent).bookBusTicket(argument.capture());
    
            // ARGUMENT CAPTOR FEATURE
            assertEquals(new Long(2400), argument.getValue().getDistance());
        }
    
    }
    
    


  6. Spy/Partial Mocks

    There are some rare  where you want to mock some method of implementation.For such scenarios,Mockito has provided spy feature.

    package com.kunaal.spy;
    
    import static org.junit.Assert.assertEquals;
    import static org.mockito.Matchers.anyInt;
    import static org.mockito.Mockito.spy;
    import static org.mockito.Mockito.when;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.junit.Test;
    
    /**
     * Test case showing usage of spy feature
     * 
     * @author ktrehan
     */
    public class SpyTest {
    
        /**
         * Spy feature usage
         */
        @Test
        public void spyUsage() {
            List<String> spyList = spy(new ArrayList<String>());
            spyList.add("Kunaal");
            spyList.add("Kunaal1");
    
            when(spyList.get(anyInt())).thenReturn("Hello World");
            assertEquals(spyList.size(), 2);
            assertEquals(spyList.get(34), "Hello World");
        }
    }
    
    

    Here in the above method,we are creating a partial mock of ArrayList class.
    Here we are invoking actual implementation of add(..) method.However get(..) method is mocked.
  7.  
  8. Mockito annotations

    Mockito has annotations for most of it features.These are:-
    @Mock                         This is equivalent to mock(.....)

    @InjectMocks              This injects mock objects in the object

    @Spy                             This is equivalent of spy(.....)

    @Captor                         This is equivalent of ArgumentCaptor.forClass(.....)


    However for these annotations to work,either our test class should use
    @RunWith(MockitoJUnitRunner.class) or in the setUp() or @Before method
               MockitoAnnotations.initMocks(...); should be added
  9.  

How to test void() method in Mockito?
Not all methods returns something.Some methods does not return anything.

Use case has following classes.These are:-
  • EntityModel             - Model entity class
  • IEntityDAO             - Interface for persisting,updating and deleting the model information in
                                       data store.
  • IEntityService          - Service interface for persisting,updating and deleting the data
  • EntityServiceImpl    - Service implementation class having dependency IEntityDAO for
                                        persisting,updating and deleting the data.
 
EntityModel.java

 Model class for storing data information.
package com.kunaal.model;

import java.io.Serializable;
import java.util.Map;

/**
 * Model class for entity
 * 
 * @author ktrehan
 */
public class EntityModel implements Serializable {
    /**
     * Variable for primary key
     */
    private Object primaryKey;

    /**
     * Variable for storing attribute name and value in a map
     */
    private Map<String, Object> attributeMap;

    /**
     * Default Constructor
     */
    public EntityModel() {
    }

    /**
     * @return the primaryKey
     */
    public Object getPrimaryKey() {
        return primaryKey;
    }

    /**
     * @param primaryKey
     *                the primaryKey to set
     */
    public void setPrimaryKey(Object primaryKey) {
        this.primaryKey = primaryKey;
    }

    /**
     * @return the attributeMap
     */
    public Map<String, Object> getAttributeMap() {
        return attributeMap;
    }

    /**
     * @param attributeMap
     *                the attributeMap to set
     */
    public void setAttributeMap(Map<String, Object> attributeMap) {
        this.attributeMap = attributeMap;
    }
}



IEntityDAO.java
DAO interface for persisting,deleting and updating the data in data store.
package com.kunaal.dao;

import com.kunaal.model.EntityModel;

/**
 * DAO interface for persisting,updating and deleting the model
 * 
 * @author ktrehan
 */
public interface IEntityDAO {

    /**
     * Method for insertion the data
     * 
     * @param model
     * @return
     */
    public Object insert(EntityModel model);

    /**
     * Method for updating the data
     * 
     * @param model
     */
    public void update(EntityModel model);

    /**
     * Method for deleting the data
     * 
     * @param model
     * @return
     */
    public boolean delete(EntityModel model);

}




IEntityService.javaService interface for insertion,deletion and updation.
package com.kunaal.service;

import com.kunaal.model.EntityModel;

/**
 * Service interface for insert,update and deletion
 * 
 * @author ktrehan
 */
public interface IEntityService {
    /**
     * Method for insertion the data
     * 
     * @param model
     * @return
     */
    public Object insert(EntityModel model);

    /**
     * Method for updating the data
     * 
     * @param model
     */
    public void update(EntityModel model);

    /**
     * Method for deleting the data
     * 
     * @param model
     * @return
     */
    public boolean delete(EntityModel model);
}



EntityServiceImpl.java
Service implementation having dependency on DAO class which handles insertion,updation and deletion logic.

package com.kunaal.service.impl;

import com.kunaal.dao.IEntityDAO;
import com.kunaal.model.EntityModel;
import com.kunaal.service.IEntityService;

/**
 * Service implementation delegating the calls to DAO
 * 
 * @author ktrehan
 */
public class EntityServiceImpl implements IEntityService {

    // Variable for entityDAO
    private IEntityDAO entityDAO;

    /**
     * Insert method
     */
    public Object insert(EntityModel model) {
        return getEntityDAO().insert(model);
    }

    /**
     * Update method
     */
    public void update(EntityModel model) {
        getEntityDAO().update(model);
    }

    /**
     * Delete method
     */
    public boolean delete(EntityModel model) {
        return getEntityDAO().delete(model);
    }

    /**
     * @return the entityDAO
     */
    public IEntityDAO getEntityDAO() {
        return entityDAO;
    }

    /**
     * @param entityDAO
     *                the entityDAO to set
     */
    public void setEntityDAO(IEntityDAO entityDAO) {
        this.entityDAO = entityDAO;
    }

}


EntityServiceTest.java
Test class for EntityServiceImpl.java


package com.kunaal.service;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import java.util.Map;

import org.junit.BeforeClass;
import org.junit.Test;

import com.kunaal.dao.IEntityDAO;
import com.kunaal.model.EntityModel;
import com.kunaal.service.impl.EntityServiceImpl;

/**
 * @author ktrehan
 * 
 */
public class EntityServiceTest {

    // Variable for entity service
    private static EntityServiceImpl entityService;

    // Variable for entity DAO
    private static IEntityDAO entityDAO;

    /**
     * Static initializer where setup happens
     */
    @BeforeClass
    public static void setup() {
        entityDAO = mock(IEntityDAO.class);
        entityService = new EntityServiceImpl();
        entityService.setEntityDAO(entityDAO);
    }

    /**
     * Test method for void update method
     */
    @Test
    public void update() {
        EntityModel model = new EntityModel();
        Map<String, Object> attributeMap = model.getAttributeMap();

        model.setPrimaryKey(new Long(1));
        attributeMap.put("Name", "TestCase");
        attributeMap.put("Address", "India");

        // MOCKING VOID METHOD UN ENTITYDAO
        doNothing().when(entityDAO).update(model);
        entityService.update(model);

        assertEquals(new Long(1), model.getVersion());
        verify(entityDAO, times(1)).update(model);
    }
}


Here we are faking update(..) method present under IEntityDAO.Update method is a void method.
So syntax for faking is little different .Instead of when(Mock.method(...).thenReturn(...) ,we are using doNothing().when(Mock.method(...))

Restrictions of Mockito

Mockito can't do following things.These are:
  • can't mock static classes
  • can't mock final classes
  • can't mock static methods
  • can't mock equals and hashCode

Old method of overcoming Mockito Limitations

Since Mockito and other such frameworks can't mock static,final  classes and methods.So developers use to write reflection utility to over come this problem

This reflection utility serves following purposes.These are:-
  • Setting values for static final variables
  • Setting field value of static variable
  • Setting field value of private variable
  • Getting field value of private variable
  • Getting response from private  method invocation.

ReflectionUtility.java 
package com.kunaal;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * @author ktrehan
 * 
 */
public class ReflectionUtil {

    /**
     * Utility method to find value of private field
     * 
     * @param fieldName
     * @param instance
     * @return
     * @throws SecurityException
     * @throws NoSuchFieldException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static Object getFieldValue(Class classVal, String fieldName,
            Object instance) throws Exception {
        Field field = classVal.getDeclaredField(fieldName);
        field.setAccessible(true);
        return field.get(instance);
    }

    /**
     * Utility method for finding value of private method invocation.
     * 
     * @param methodName
     * @param instance
     * @return
     * @throws SecurityException
     * @throws NoSuchMethodException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    public static Object getMethodValue(Class classVal, String methodName,
            Object instance, Class[] classArgArr, Object[] valArgArr)
            throws Exception {
        Method method = null;
        Object returnVal = null;

        if (classArgArr == null || classArgArr.length == 0) {
            method = classVal.getDeclaredMethod(methodName, null);
        } else {
            method = classVal.getDeclaredMethod(methodName,
                    classArgArr);
        }
        method.setAccessible(true);

        if (valArgArr == null || valArgArr.length == 0) {
            returnVal = method.invoke(instance, null);
        } else {
            returnVal = method.invoke(instance, valArgArr);
        }
        return returnVal;
    }

    /**
     * Utility method to set value of private field
     * 
     * @param fieldName
     * @param instance
     * @return
     * @throws SecurityException
     * @throws NoSuchFieldException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static void setFieldValue(Class classVal, String fieldName,
            Object instance, Object mockVal) throws Exception {
        Field field = classVal.getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(instance, mockVal);

    }

    /**
     * Utility method for setting static field value
     * 
     * @param classVal
     * @param fieldName
     * @param mockVal
     * @throws Exception
     */
    public static void setStaticFieldValue(Class classVal,
            String fieldName, Object mockVal) throws Exception {
        Field field = classVal.getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(null, mockVal);
    }

    /**
     * Utility method for setting static final variable
     * 
     * @param classVal
     * @param fieldName
     * @param mockVal
     * @throws Exception
     */
    public static void setFinalStatic(Class classVal, String fieldName,
            Object mockVal) throws Exception {
        Field field = classVal.getDeclaredField(fieldName);
        field.setAccessible(true);

        // remove final modifier from field
        Field modifiersField = Field.class
                .getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers()
                & ~Modifier.FINAL);

        field.set(null, mockVal);
    }

}


Use Case:-

Here SchoolAdmission class follows template pattern.
Method admissionStatus(.....) calls  following private methods.
  • submitForm(AdmissionDataModel)
  • formVerified(AdmissionDataModel)
  • ageConditionMet(AdmissionDataModel)
  • parentQualMet(AdmissionDataModel)
  • feesConditionsAgreed(AdmissionDataModel)




AdmissionDataModel.java
Model class for kid admission attributes.

package com.kunaal.admission;

import java.util.Calendar;
import java.util.Map;

/**
 * Model class for kid admission attributes
 * 
 * @author ktrehan
 */
public class AdmissionDataModel {
    
    /**
     * Variable for kid name
     */
    private String kidName;
    
    /**
     * Variable for parent qualifications
     */
    private Map<String,String> parentQualMap;
    
    /**
     * Variable for parent's approval for annual fee hike if any
     */
    private Boolean annualFeeHike;
    
    /**
     * Variable for kid's date of birth
     */
    private Calendar dob;
    
    /**
     * Variable for checking whether form is fully filled or not
     */
    private Boolean formFullyFilled;
    
    /**
     * Variable for form submission date
     */
    private Calendar formSubDate;

    /**
     * Parameterized constructor
     * 
     * @param kidName
     * @param parentQualMap
     * @param annualFeeHike
     * @param dob
     * @param formFullyFilled
     * @param formSubDate
     */
    public AdmissionDataModel(String kidName,
            Map<String, String> parentQualMap, Boolean annualFeeHike,
            Calendar dob, Boolean formFullyFilled, Calendar formSubDate) {
        this.kidName = kidName;
        this.parentQualMap = parentQualMap;
        this.annualFeeHike = annualFeeHike;
        this.dob = dob;
        this.formFullyFilled = formFullyFilled;
        this.formSubDate = formSubDate;
    }

    /**
     * @return the kidName
     */
    public String getKidName() {
        return kidName;
    }

    /**
     * @return the parentQualMap
     */
    public Map<String, String> getParentQualMap() {
        return parentQualMap;
    }

    /**
     * @return the annualFeeHike
     */
    public Boolean getAnnualFeeHike() {
        return annualFeeHike;
    }

    /**
     * @return the dob
     */
    public Calendar getDob() {
        return dob;
    }

    /**
     * @return the formFullyFilled
     */
    public Boolean getFormFullyFilled() {
        return formFullyFilled;
    }

    /**
     * @return the formSubDate
     */
    public Calendar getFormSubDate() {
        return formSubDate;
    }
    
    
}


SchoolAdmission.java
package com.kunaal.admission;

import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.Map;

/**
 * Template class for school admission
 * 
 * @author ktrehan
 */
public class SchoolAdmission {

    /**
     * Method for finding admission status
     * 
     * @param dataModel
     * @return
     */
    public String admissionStatus(AdmissionDataModel dataModel) {
        StringBuffer buffer = new StringBuffer();

        boolean submitForm = submitForm(dataModel);
        if (!submitForm) {
            buffer.append("Form not submitted on time");
        }

        boolean formVerified = formVerified(dataModel);
        if (!formVerified) {
            buffer.append("Form is not filled properly");
        }

        boolean ageConditionMet = ageConditionMet(dataModel);
        if (!ageConditionMet) {
            buffer.append("Your kid does not fulfil age conditions");
        }

        boolean parentQualMet = parentQualMet(dataModel);
        if (!parentQualMet) {
            buffer.append("Basic parent's qualification not met");
        }

        boolean feesConditionsAgreed = feesConditionsAgreed(dataModel);
        if (!feesConditionsAgreed) {
            buffer.append("Special condition of annual fee hike(if any) is not checked in form");
        }

        if (buffer.length() == 0)
            buffer.append("Your kid's admission status - SUCCESS");

        return buffer.toString();
    }

    /**
     * Private method for submitting admission form
     * 
     * @param dataModel
     * @return
     */
    private boolean submitForm(AdmissionDataModel dataModel) {
        Calendar formSubDate = dataModel.getFormSubDate();
        Calendar finalDate = GregorianCalendar.getInstance();
        finalDate.set(Calendar.YEAR, 2013);
        finalDate.set(Calendar.MONTH, Calendar.JANUARY);
        finalDate.set(Calendar.DAY_OF_MONTH, 1);

        if (formSubDate.before(finalDate)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Private method for verifying the form
     * 
     * @param dataModel
     * @return
     */
    private boolean formVerified(AdmissionDataModel dataModel) {
        return true;
    }

    /**
     * Private method for age condition check
     * 
     * @param dataModel
     * @return
     */
    private boolean ageConditionMet(AdmissionDataModel dataModel) {
        Calendar kidDob = dataModel.getDob();
        Calendar finalDate = GregorianCalendar.getInstance();

        int birthYear = kidDob.get(Calendar.YEAR);
        int currYear = finalDate.get(Calendar.YEAR);

        if ((currYear - birthYear) >= 4) {
            return true;
        } else {
            return false;
        }

    }

    /**
     * Private method for parent qualification check
     * 
     * @param dataModel
     * @return
     */
    private boolean parentQualMet(AdmissionDataModel dataModel) {
        Map<String, String> parentQualMap = dataModel
                .getParentQualMap();
        boolean result = false;
        Collection<String> values = parentQualMap.values();

        for (String data : values) {
            if (data.toString().equals("MBA")) {
                result = true;
                break;
            }
        }

        return result;
    }

    /**
     * Private method for fees conditions check
     * 
     * @param dataModel
     * @return
     */
    private boolean feesConditionsAgreed(AdmissionDataModel dataModel) {
        return true;
    }
}


Unit Test case for testing private method parentQualMet() using reflection utility

package com.kunaal.admission;

import static org.junit.Assert.assertTrue;

import java.util.HashMap;
import java.util.Map;

import org.junit.BeforeClass;
import org.junit.Test;

import com.kunaal.ReflectionUtil;

/**
 * This test illustrates testing of private methods using reflection api
 * 
 * @author ktrehan
 */
public class SchoolAdmissionTest {

    /**
     * Variable for SchoolAdmission
     */
    private static SchoolAdmission schoolAdmission;

    /**
     * Before class set up
     */
    @BeforeClass
    public static void setUp() {
        schoolAdmission = new SchoolAdmission();
    }

    /**
     * Testing private method parentQualMet()
     * 
     * @throws Exception
     */
    @Test
    public void parentQualMet() throws Exception {
        AdmissionDataModel dataModel = new AdmissionDataModel(null,
                null, null, null, null, null);
        // Setting dummy map
        Map<String, String> dummyMap = new HashMap<String, String>();
        dummyMap.put("Father", "MBA");

        // Set the particular private field
        ReflectionUtil.setFieldValue(AdmissionDataModel.class,
                "parentQualMap", dataModel, dummyMap);

        // Invoking private method
        Class[] classArgArr = new Class[] { AdmissionDataModel.class };
        Object[] valArgArr = new Object[] { dataModel };

        Object methodValue = ReflectionUtil.getMethodValue(
                SchoolAdmission.class, "parentQualMet",
                schoolAdmission, classArgArr, valArgArr);

        assertTrue(methodValue instanceof Boolean);
        assertTrue((Boolean) methodValue);
    }

}



Here we are using reflection to set value for private variable "parentQualMap" and invoking private method "parentQualMet".However now we have PowerMock   which does it nicely


PowerMock and its features

PowerMock is a framework which uses a custom classloader and bytecode manipulation to enable mocking of static methods, constructors, final classes and methods, private methods, removal of static initializers and more. By using a custom classloader no changes need to be done to the IDE or continuous integration servers which simplifies adoption.

PowerMock Features
  • Mocking static methods
  • Mocking private methods
  • Mocking new object creation inside method
  • Mocking final

Lets revisit the use case described above and use PowerMock for invoking private method and setting private variables

SchoolAdmission1.java

Testcase using Whitebox utility provided by PowerMock for testing private methods.

package com.kunaal.admission;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.HashMap;
import java.util.Map;

import org.junit.BeforeClass;
import org.junit.Test;
import org.powermock.reflect.Whitebox;

/**
 * This test illustrates the use of power mock whitebox method for testing
 * private methods and setting values in private fields
 * 
 * @author ktrehan
 */
public class SchoolAdmission1 {

    /**
     * Variable for SchoolAdmission
     */
    private static SchoolAdmission schoolAdmission;

    /**
     * Before class set up
     */
    @BeforeClass
    public static void setUp() {
        schoolAdmission = new SchoolAdmission();
    }

    /**
     * Testing private method parentQualMet()
     * 
     * @throws Exception
     */
    @Test
    public void parentQualMet() throws Exception {
        AdmissionDataModel dataModel = new AdmissionDataModel(null,
                null, null, null, null, null);

        Map<String, String> dummyMap = new HashMap<String, String>();
        dummyMap.put("Father", "MBA");

        // Whitebox usage for setting private field
        Whitebox.setInternalState(dataModel, "parentQualMap", dummyMap);

        // Whitebox usage for invoking private methods
        Boolean invokeMethod = Whitebox.<Boolean> invokeMethod(
                schoolAdmission, "parentQualMet", dataModel);

        assertEquals(dataModel.getParentQualMap(), dummyMap);
        assertTrue(invokeMethod);
    }

}


Use case of mocking a static method

In this use case we will unit test a static method.Here we are taking example of Singleton class where getInstance()  and getData() method are static methods.

Singleton.java
Singleton class having getInstance() and getData() as static methods.

package com.kunaal.singleton;

/**
 * Singleton class example
 * 
 * @author ktrehan
 */
public class Singleton {

    /**
     * Variable for singleton
     */
    private static Singleton INSTANCE = new Singleton();

    /**
     * Private constructor
     */
    private Singleton() {
    }

    /**
     * Static method for getting instance object
     * 
     * @return
     */
    public static Singleton getInstance() {
    return INSTANCE;
    }

    /**
     * Dummy static getData() method
     * 
     * @return
     */
    public static String getData() {
    return null;
    }

}


SingletonTest.java
Test case for singleton class using PowerMock.

package com.kunaal.singleton;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

/**
 * Test class illustrating usage of mocking static method using power mock
 * 
 * @author ktrehan
 */
@RunWith(PowerMockRunner.class)
public class SingletonTest {

    /**
     * Testing getInstance() of Singleton
     */
    @Test
    @PrepareForTest(Singleton.class)
    public void getInstance() {
    PowerMockito.mockStatic(Singleton.class); // Mock all the static methods
                          // of Singleton

    Singleton mockObj = mock(Singleton.class); // Mock the singleton class
    when(Singleton.getInstance()).thenReturn(mockObj); // Push the
                               // expectations

    assertEquals(Singleton.getInstance(), mockObj);// Assertions
    PowerMockito.verifyStatic(times(1));// Verification for each method
    Singleton.getInstance();

    PowerMockito.verifyStatic(Mockito.never());// Verification for each
                           // method
    Singleton.getData();
    }
}


Use case of mocking object creation

InnerObject.java
Here in listSize() method, we are creating a new object.Depending upon the object state we are returning true/false.
package com.kunaal.mocknew;

import java.util.ArrayList;
import java.util.List;

/**
 * Class which creates a new object inside a method
 * 
 * @author ktrehan
 */
public class InnerObject {

    /**
     * Method for finding list size where object is created inside
     * 
     * @return
     */
    public boolean listSize(){
        List<String> dataList=new ArrayList<String>();
        if(dataList.size() >0)
            return false;
        else
            return true;        
    }
}


InnerObjectTest.java
This test class shows the usage of PowerMock ability to mock new object creation.

package com.kunaal.mocknew;

import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;

import java.util.ArrayList;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

/**
 * Test case showing usage of Power Mock new object feature Here in
 * InnerObject's listSize() method we create a new array list object This is
 * mocked here
 * 
 * @author ktrehan
 */
@RunWith(PowerMockRunner.class)
public class InnerObjectTest {

    @Test
    @PrepareForTest(InnerObject.class)
    public void listSize() throws Exception {
    ArrayList mockList = mock(ArrayList.class); // mock arraylist object
                            // which is returned
                            // by constructor invocation

    PowerMockito.whenNew(ArrayList.class).withNoArguments()
        .thenReturn(mockList);// Using PowerMock to mock
                      // arraylist constructor
    when(mockList.size()).thenReturn(0);// setting expectation of mock

    InnerObject obj = new InnerObject();
    assertTrue(obj.listSize());

    Mockito.verify(mockList, Mockito.times(1)).size();
    // Using PowerMock to verify constructor invocation
    PowerMockito.verifyNew(ArrayList.class, times(1)).withNoArguments();
    }

}


Use case of mocking private methods

PrivateCall.javaHere  we have a public method which internally uses private method for some computation.

package com.kunaal.mockprivate;

import java.util.List;

/**
 * Class where a public method outsourced main logic to private method
 * 
 * @author ktrehan
 */
public class PrivateCall {
    
    /**
     * Public method which invokes private method for business logic 
     * 
     * @param inputList
     * @param valToChk
     * @return
     */
    public boolean find(List<String> inputList,String valToChk){
        if(inputList !=null)
            return isExists(inputList, valToChk);
        else
            return false;
    }

    /**
     * Private method having main logic
     * 
     * @param inputList
     * @param valToChk
     * @return
     */
    private boolean isExists(List<String> inputList,String valToChk){
        return inputList.contains(valToChk);
    }
}


PrivateCallTest.java
Test class which uses PowerMock to mock the private method .
package com.kunaal.mockprivate;

import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

/**
 * Test case showing usage of Power Mock's private method testing
 * 
 * @author ktrehan
 */
@RunWith(PowerMockRunner.class)
public class PrivateCallTest {

    @Test
    @PrepareForTest(PrivateCall.class)
    public void find() throws Exception {
    PrivateCall underTest = PowerMockito.spy(new PrivateCall());// Using
                                    // power
                                    // mock spy
                                    // feature

    // Input data
    List<String> inputList = new ArrayList<String>();
    String valToChk = new String();

    // Setting expectation of private method call
    PowerMockito.when(underTest, "isExists", inputList, valToChk)
        .thenReturn(Boolean.TRUE);

    assertTrue(underTest.find(inputList, valToChk));

    // Optionally verify that the private method was actually called
    PowerMockito.verifyPrivate(underTest).invoke("isExists", inputList,
        valToChk);
    }

}


6 comments:

  1. Awesome post...it covers the whole of unit testing in a very simple and extensive manner....thanks

    ReplyDelete
  2. The article was nice and explanation is clear and more realistic

    ReplyDelete
  3. Awsome article man...... great job :)

    ReplyDelete
  4. Could you please write some example on nodejs.

    ReplyDelete
  5. nice article...................................

    ReplyDelete
  6. I have read your blog its very attractive and impressive. I like it your blog.

    Java Online Training Java EE Online Training Java EE Online Training Java 8 online training Java 8 online training

    Java Online Training from India Java Online Training from India Core Java Training Online Core Java Training Online Java Training InstitutesJava Training Institutes

    ReplyDelete