JMockit: A beginner’s guide for Unit Testing


So we are following TDD, And we are working on unit test cases, And then we are stuck with mocking.
Well this was the situation we faced. And while going through various API tools for mocking, we came through this amazing toolkit: JMockit.

What is JMockit?

JMockit is open source software licensed under the MIT License. It includes APIs for mocking, faking, and integration testing, and a code coverage tool. The library is meant to be used together with a testing framework such as JUnit or TestNG.

Why JMockit?

Well the most important reason to opt for JMockit is because it lets us mock anything. And we mean it. Literally anything.

imeanit

  • JMockit provides us APIs to mock:
  1. Public Methods with void and non-void return types
  2. Private Methods with void and non-void return types
  3. Field Values
  4. Static Methods
  5. Static Blocks
  6. Constructors
  • JMockit provides ways to record and verify the mocked values
  • JMockit provides a lot of useful and powerful annotations to ease down the testing. For example: @Tested @Injectable, we will be covering few of them in this blog.

Now let us dive into the ocean called JMockit.
Step 1: Annotations: As we mentioned, there are a number of annotations, so let’s see the most frequently used:

  • @Tested: This annotation is used to get an initialized object of the class that we want to test along with the injection of the dependencies.
  • @Mocked: This annotation is used to provide a mock field/parameter for the class being tested. The type of the mock field or parameter can be any kind of reference type: an interface, a class (including abstract and final ones), an annotation, or an enum. But primitive and array types are not provided (to the class being tested) using @Mocked. A mocked instance of that type is automatically created and assigned to the mock field/parameter.
  • @Injectable: Indicates that the value of a mock field or mock parameter will be an isolated mocked instance, intended to be passed or injected into the code under test. Such instances can be said to be proper mock objects, in contrast to the mocked instances of a regular @Mocked type.

So now we got the definitions of both, but in common terms what’s the basic difference between @Mocked and @Injectable?

lost

Well!! The easiest way to differentiate is:

  1. @Mocked will mock everything and all instances of that class, and @Injectable will only mock a specific method/field of one instance of that class.
  2. When you have done explicit injection (like using Guice), you have to use @Injectable, otherwise you will get:
    java.lang.IllegalArgumentException: No constructor in tested class that can be satisfied by available tested/injectable values.

 

Step 2: JMockit provides three mocking APIs that helps us achieve the mocking (or faking) methods, fields constructors and statics, we discussed above.

  1. MockUp<>() API
  2. Expectations() API
  3. Deencapsulation API

So first thing is first, Let’s undergo the key difference between the three:

MockUp<>()

  1. In MockUp the parameter values doesn’t matter, we provide (override with) a fake implementation of the method. And whenever the method is called, this mocking is used.
  2. Once the MockUp is provided, it will be called as many times that particular method is called.
  3. The order of MockUps (when multiple methods are mocked) is not important.

Expectations()

  1. In Injectable the values we are passing decides the result of that method. So we use it when, for different values, we “expect” different values.
  2. If the same method (whose expectations is given) has multiple calls, we have to provide “number of calls” otherwise we would get exception. There are three ways to provide number of repetitions: times (for providing exact number of times), minTimes (for providing the least number of times it should be called), maxTimes (for providing the at-most number of times allowed).
  3. The order of Expectations provided for multiple methods is critically important. It has to be in the same order as the methods have been called in the actual code.

Deencapsulation

  1. Deencapsulation provides method for reflecting and calling private methods, fields and instances of objects being used in the Tested class.
  2. It has methods to set field values, to invoke private methods, get instance for an inner class etc.

Ok now let’s have a small cheat sheet of when to use what:

  1. Behavior (parameter values) matters, use Expectations else use MockUp.
  2. Number of calls matter, use Expectations else use MockUp.
  3. Faking static blocks, static methods or constructors, Use MockUp.
  4. Want to test private methods and mock inner classes, use Deencapsulation.

 

Now let’s have an example:

First of all, we would be needing the dependencies for using JMockit and JUnit.


<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.33</version>
<scope>test</scope>
</dependency>


NOTE:
We have to put JMockit’s dependency above Junit’s dependency, else it would not be able to work with the JUnit framework. Same goes for TestNG.
First we have a class that has static method:

 

package com.knoldus.jmockit.example;

public class NameValidator {

    private NameValidator() {

    }

    public static Boolean isNameValid(String name) {
        return name.matches("^[a-zA-Z]+$");
    }

}

 

Then we have a class that can be injected as dependency:

package com.knoldus.jmockit.example;

public class Allocator {
    String allocationValue;

    Allocator(String value) {
        this.allocationValue = value;
    }

    public String getAllocationValue() {
        return this.allocationValue;
    }

    public String getAllocation(String name) {
        if (name.equalsIgnoreCase("knolder")) {
            return "Welcome to Knoldus";
        } else {
            return "Not allocated";
        }
    }
}

 

 

Now here’s the class that we will be testing. We have injected Allocator class in Person class, to be used further, and we will be calling the static method of NameValidator.

package com.knoldus.jmockit.example;

import com.google.inject.Inject;

public class Person {

    private Allocator allocator;

    @Inject
    Person(Allocator allocator) {
        this.allocator = allocator;
    }

    public String getDetails(String name) {
        return allocator.getAllocation(name);
    }

    public String setName(String name) {
        if (NameValidator.isNameValid(name)) {
            return name;
        } else {
            return "DEFAULT";
        }
    }

    public String getAllocationValue() {
        return allocator.getAllocationValue();
    }

}

 

 

Now while working on unit test cases: We have the following imports:

import mockit.Deencapsulation;
import mockit.Expectations;
import mockit.Injectable;
import mockit.Mock;
import mockit.MockUp;
import mockit.Tested;
import org.junit.Test;

 

 import static org.junit.Assert.assertEquals; 

Since we are testing Person class and injecting Allocator, we used:

@Tested
Person person;

@Injectable
Allocator allocator;

 

  1.  In the first test we will use MockUp to mock static method of NameValidator such that even after giving an invalid value we will get a desired value:
    @Test
    public void setPersonShouldReturnValidNameWithMocking() {
        new MockUp&amp;amp;amp;lt;NameValidator&amp;amp;amp;gt;() {
            @Mock
            Boolean isNameValid(String name) {
                return true;
            }
        };
        String actualResult = person.setName("123");
        String expectedValidResult = "123";
        assertEquals("expected value does not match to the actual value",
                expectedValidResult, actualResult);
    }
    
  2. In the next test we will use Expectations to mock a method of Allocator such that even after giving a valid value we will get the message we provide.
    @Test
    public void getDetailsShouldReturnNotAllocatedMessage() {
        new Expectations() {
            {
                allocator.getAllocation("knolder");
                result = "Not allocated";
                maxTimes = 1;
            }
        };
        String actualResult = person.getDetails("knolder");
        String expectedResult = "Not allocated";
        assertEquals("expected value does not match to the actual value",
                expectedResult, actualResult);
    }
    

    Here instead of maxTimes, we can use minTimes and times depending of our logical scenario.

  3. In the last test case we wanted to use a particular instance of Allocator with a particular allocationValue, but since it is a private member, we can’t use Expectations or MockUp, So we moved to Deencapsulation for help.
    @Test
    public void getAllocationShouldReturnDefaultValue() {
            Deencapsulation.setField(person,"allocator", new Allocator("nothing"));
        assertEquals("value not matched", "nothing", person.getAllocationValue
                ());
    }
    

     

     

Hope this attempt would be helpful to you. For more doubts and examples regarding various technologies, feel free to go through our blogs, because we at Knoldus believe in gaining knowledge and growing our skills together.

References: http://jmockit.org/

About anmolmehta

Technology Enthusiast
This entry was posted in Java, JUnit and tagged , , , , , , . Bookmark the permalink.

One Response to JMockit: A beginner’s guide for Unit Testing

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s