JMockit Quick Tutorial and Revelation

JMockit is a powerful mocking framework in Java that provides extended functionality that goes beyond the capabilities of all other solutions on the market so far. While most mocking frameworks are proxy based and depend on Reflection to hide the dependent behavior, JMockit falls further and deeper with solving some critical problems such as mocking final classes and static methods by using the Instrumentation API presented in Java 5.

The Instrumentation API allows a direct bytecode replacement in the virtual machine via java agents that modify the codebase of the current classes with the one from our mocked representation (either empty stubs or entirely modified behavior). This is a powerful process that could be hardly debugged even with JMockit codebase – just because the whole lifecycle in the JVM is changed with the agents that do the low level modification (basically done with ASM in the mocking framework).

JMockit Components

JMockit toolkit contains 6 important separated components to be used for mocking reasons. They are:

  • JMockit Expectations
  • JMockit Verifications
  • JMockit State Oriented (Mockups)
  • JMockit Coverage
  • JMockit Hibernate Emulation
  • JMockit AOP

The AOP part is the dark power of that century – just like the other mystic words like Cloud or SaaS or Business Component/Entity. It gives some injection abilities in addition to the standard Mocking approach.

The Hibernate Emulation represents some fake mocks to the Hibernate 3 API, which is however still unstable and not ready (after some research on StackOverflow I found few comments from the author himself discussing the ineffectiveness of some parts of the implementation).

It took me few days to find any reasonable documentation, tutorial and/or codebase for starters. The only resources I found usable for intro to jmockit were Dhruba’s examples and some other random code snippets around. The tutorial from the Google Code repository starts with business entities and hardcore logic at first place with no details explained from scratch.

Let’s find out how Expectations, Verifications and Mockups work. With a dummy demo that shows the usage of all of them at a place.

Sample case

We have a bank system. A person has a name and some amount of money. We have a Bank object that calculates a resulting sum for a person based on some InterestCalculator. The bank has an interest rate and the calculator executes an algorithm to define additional rate based on the sum that a person owns. Pretty much that’s our sample scenario and here there are our demo classes:

Person

public class Person {
	private String name;
	private double currentSum;

	public Person() {
		super();
	}
	public Person(String name, double currentSum) {
		super();
		this.name = name;
		this.currentSum = currentSum;
	}
	public String getName() {
		return name;
	}
// more getters and setters
}

Bank

public class Bank {
	private double bankInt;
	private String name;
	private InterestCalculator calc;

	public Bank() {
		this("Test Bank", 0.15d);
	}
	public Bank(String name, double bankInt) {
		super();
		this.name = name;
		this.bankInt = bankInt;
	}

	// count the sum with the interest for a month but after the bank fees
	public double getSumAfterInterest(Person p) {
		double currentSum = p.getCurrentSum();
		double interest = InterestCalculator.getInterest(currentSum);

		// count current sum + the interest and keep in mind bank fees 
		double result = (currentSum + interest) * (1-bankInt);

		return result;
	}
// get/set methods
// ...
}

Interest Calc

public class InterestCalculator {
	// dummy interest counter
	// large interest for small sums
	public static double getInterest(double sum) {
		if(sum < 1000) {
			return sum * 0.3;
		}
		else if(sum >= 1000 && sum < 10000) {
			return sum * 0.2;
		}
		return 0.1;
	}
}

We have set a TestNG project with JMockit integration and we want to test our demo without relying on the InterestCalculator. Assuming that this is a class from an external library and we might not have access to him, we will presume three different cases:

  1. the InterestCalculator is called twice and the result is 200 after the first call and 400 after the second
  2. the InterestCalculator method is called between 0 and 5 times
  3. the InterestCalculator will use a predefined algorithm that we could modify for the given test

Our demo will mock the InterestCalculator but we are going to use the mocked data through the invocations in the Bank class

Two calls – 200/400

Using the Expectations API we expect that 2 calls of the getInterest(…) method would be done and returned values will be 200d and 400d

public void testFlowMockedInterest() {
	  new Expectations() {
		  InterestCalculator interest;
		  {
			  interest.getInterest(anyDouble); result = 200.0;
			  interest.getInterest(anyDouble); result = 400.0;
		  }
	  };

	  Person person = new Person("John", 1000);
	  Bank bank = new Bank("Alpha", 0.2);

	  Assert.assertTrue(bank.getSumAfterInterest(person) == 960.0);
	  Assert.assertTrue(bank.getSumAfterInterest(person) == 1120.0);
  }

Expectations are strict here – so they will be executed in exactly the same order and any additional call to the mocked object (or missing call) will lead to exception. We could relace it with NonStrictExpectations class to just omit that obligation. Also, we haven’t defined our field as @Mocked because this annotation is applied by default in this case (all fields defined in the anonymous class are being mocked).

Number of calls

If we ignore the exact results but we only need to check the number of invocations, we might test that through Verifications.

  @Mocked InterestCalculator interest;
  @Test(enabled=true)
  public void testFlowVerifiedInterest() {
	  Person person = new Person("John", 1000);
	  Bank bank = new Bank("Alpha", 0.2);

	  bank.getSumAfterInterest(person);
	  bank.getSumAfterInterest(person);
	  bank.getSumAfterInterest(person);
	  bank.getSumAfterInterest(person);

	  new Verifications() {
		  {
			  interest.getInterest(anyDouble); minTimes = 0; maxTimes = 5;
		  }
	  };
  }

Note: for direct mocking we could add the class as a method parameter. However as we have indirect call through the Bank class, we should define a @Mocked declaration which is visible for all test methods. If you have different definitions in other test methods, remove the Mocked class field when executing other tests.

We could also mix the usage of Expectations and Verifications. Just don’t repeat the same code in both blocks cause this would lead to … well, mistake. Anyway, you could define the mocked class in the Expectations block and omit the usage of the class field. There are also three more types of verifications – InOrder, Full and both (FullVerificationsInOrder).

Specific behavior

Sometimes we need to declare a more complex logic or just to redefine the class we would normally use in an application. This is the chance for creating a class – replacer as a MockUp where we could define our logic:

 @Test(enabled=true)
  public void testStubbedInterest() {
	  new MockUp() {
		  int percentage;
		  @Mock
		  public double getInterest(double sum) {
			  return 0.1 * ++percentage;
//			  hacky, nah? 
//			  return new Random().nextDouble() * 100;
		  }
	};

	  Person person = new Person("John", 1000);
	  Bank bank = new Bank("Alpha", 0.2);

	  System.out.println(bank.getSumAfterInterest(person));
	  System.out.println(bank.getSumAfterInterest(person));
	  System.out.println(bank.getSumAfterInterest(person));
  }

Analogically we could mock the Bank class as well by using either Expectations, or Verifications or MockUp when we only expect specific calls, need to check the number of invocations or need to redefine behavior. Try it and see. And here is the demo downloadable (don’t forget to set run parameters to enable the jmockit usage and the integration of testng).

Also, check out my JMockit presentation on Slideshare:

Leave a Reply

Your email address will not be published. Required fields are marked *