Thursday, June 7, 2012

JMockit Tutorial:Learn it today with examples

JMockit is one of the many mocking frameworks available for unit testing in Java. If you have to unit test you'll invariably end up mocking objects for third party classes and for stubbing.

I assume you already know JUnit. We'll see step by step in very short tutorials that'll help you start using jmockit. I'll be trying to keep each one as crisp as possible. If you want more details.You can ask them in comments section below...

I am yet to understand some of the JMockit features completely, so please bear with me if I have not covered it in entirety.


  1. What is Mocking in unit Testing?
  2. How does JMockit support mocking?
  3. How to add JMockit to an eclipse project?
  4. How to run a test case with JMockit?
  5. How to mock default constructors(or constructors with no parameters) in JMockit?
  6. How to mock constructors with parameters (or those that take arguments) ?
  7. How to mock static initialization blocks in JMockit?
  8. How to mock public methods in JMockit
  9. How to mock  private methods in JMockit?
  10. How to mock static methods in JMockit?
  11. How to invoke or test private methods in JMockit?
  12. How to throw exceptions in an Expectations block?
  13. Some more JMockit utilities before signing off.


How to mock constructors in JMockit?

Consider the code below:


/************************* Person.java ****************/
public class Person {

 private String name;
  
 
 public Person(){
  name = "Default Abhi";
 }
 
 public String getName() {
  return name;
 }
 
 public void setName(String name) {
  this.name = name;
 }
}

/******************* PersonTestConstructor.java ***********/
public class PersonTestConstructor {

 @Test
 public void testGetName() {
  new MockUp<Person>() {
   @Mock
   public void $init() {
    //Dont assign name variable at all
    //Leave it null
   }

  };
  
  Person p = new Person();
  String name = p.getName();
  
  assertNull("Name of person is null",name);
 }

}
Here we have a class Person whose default constructor initializes the name variable to "Default Abhi" In the test class PersonTestConstructor, I mock the constructor by using a special mock method $init to do nothing.

Remember the to put the @Mock  annotation above. I have spent hours trying to debug only to find out that I forgot.

How to mock constructors with parameters (or those that take arguments) ?


Again we use $init to mock the constructor but this time with parameters.
  

/************************* Person.java ****************/
public class Person {

 private String name;
  
 public Person(String name){
  this.name = name;
 }
 
 public String getName() {
  return name;
 }
 
 public void setName(String name) {
  this.name = name;
 }
}

/******************* PersonTestConstructor.java ***********/
public class PersonTestConstructor {

 @Test
 public void testGetName() {
  new MockUp<Person>() {
   @Mock
   public void $init(String name) {
    //Dont assign name variable at all
    //Leave it null
   }

  };
  
  Person p = new Person("AbhiJMockit");
  String name = p.getName();
  System.out.println(name);
  assertNull("Name of person is null",name);
 }

}

How to mock static initialization blocks in JMockit?


This time the method to be used is $clinit. If you are bored with Person and Employee classes by now, we'll test the Bank class this time.

In the following example the static initialization block calls a static method and sets the bankBalance to 100. But in our test case we want to start off with 500.

Note the $clinit method in the BankTest class that calls the same method with 500 as the argument.


/***************** Bank.java ******************/
public class Bank {

 static int balanceAmount;
 
 //Static block begins 
 static {
  updateBalance(100);
 }
 
 public static void updateBalance(float balance) {
  balanceAmount += balance;
 }
}

/*********************** BankTest.java **********/
public class BankTest {

 @Test
 public void testBankStaticBlock(){
  
  new MockUp<Bank>(){
   
   @SuppressWarnings("unused")
   @Mock
   public void $clinit(){
    Bank.updateBalance(500);
   }
  };
  
  assertEquals("The balance amount is 500", 500, Bank.balanceAmount);
  
 }
}

How to mock public methods in JMockit?

There are two ways to can do it: 

  1. Behaviour Based Testing or using the Expectations class.
  2. State Based Testing using the MockUp apis.

We'll see both of them below. First using the Expectations class

1. Behaviour Based Testing or using the Expectations class.

In this technique we will be creating a Expectations anonymous inner class and define the expected behaviour when a method is invoked with particular arguments. 

Lets continue with the Bank example. 

This time the Bank class uses the DBManager to retrieve the Account Holder Name to process the account. 

The Bank's processAccount() method takes in an Account ID and passes it onto the DBManager's retrieveAccountHolder() method  to get the name from the DB and returns the same.

However we want to mask all the DB related code and return a name, say "Abhi" when retrieveAccountHolder() is invoked with the Account ID as 10

The code to achieve all this is given below...

/**************** DBManager.java *************/
public class DBManager {
 
 public String retrieveAccountHolderName(int accountId ){
  String accountHolderName = null;
  
  //connect to db
  //retrieve the  Account Holder Name
  
  return accountHolderName;
 }
}

/**************** Bank.java ****************/
public class Bank {

 DBManager dbManager =new DBManager();
 
  
 public String processAccount(int accountID){
  
  //Some other code goes here
  
  String accountHolderName = dbManager.retrieveAccountHolderName(accountID);
  
  //some more processing code 
  
  return accountHolderName;
 }
}

/*************** BankTest.java ****************/

public class BankTest {

 @Test
 public void testRetrieveAccountHolderName() {

  Bank bank = new Bank();
  
  // Define the Expectations block here
  new Expectations() {

   DBManager dbManager; // variables declared here are mocked by default

   {
    dbManager.retrieveAccountHolderName(10);
    returns("Abhi");
   }
  };
  
  String name = bank.processAccount(10);
  
  assertEquals("Account holder Name for A/C id 10 is 'Abhi' ","Abhi",name);
  
 }
}

Notice the DBManager is declared within the Expectations class and is mocked by default. However if you wanted to declare it outside the test method then it should be annotated with the @Mocked annotation.

Now quick trivia!
What would have happened if I placed the Bank bank = new Bank() after the
expectations block?  Any Guesses?

The answer is - "The Test would have failed because of UnexpectedInvocation Exception" Why ? The reason - Expectations is a strict checking mechanism. Placing the creation of Bank object after the Expectations block would have called the constructor of DBManager. But in the Expectations block we have strictly mentioned that :

  1. Only retrieveAccountHolderName() method  has to be called on the dbManager object in the following code.
  2. AND the argument MUST  be 10. Anything other that 10 will throw the same  UnexpectedInvocation Exception. Try this yourself.
  3. AND the method retrieveAccountHolderName() can be invoked only once!

But then, our test cases may not be so rigid. In that case use the NonStrictExpectations class.
It places no restrictions on the number of times the mocked method is called or if any other method of the same object is called. Most of the time you will be using NonStrictExpectations class.

One last tip before we move onto the MockUp apis. In the previous section point number 2 states that the argument passed must be 10.

What to do if we require, when any integer is passed into the retrieveAccountHolderName() as an argument the return string should be "Abhi"? For this JMockit provides us with a field called anyInt.   The line dbManager.retrieveAccountHolderName(10) should be replaced with dbManager.retrieveAccountHolderName(anyInt). We will look into more of this in this section.

2. State Based Testing using the MockUp apis.
If you have already read "How to mock constructors in Jmockit?" you would have seen the MockUp class in action.

MockUp apis are categorized as State Based testing because the mocks you write are not dependent on the behaviour of the test class. They are defined independently and work the same in any case.

In the code below observe how the MockUp class redefines the method retrieveAccountHolderName() of class DBManager.

This is a great utility if only some methods of a class need to be redefined.



/**************** DBManager.java *************/
public class DBManager {
 
 public String retrieveAccountHolderName(int accountId ){
  String accountHolderName = null;
  
  //connect to db
  //retrieve the  Account Holder Name
  
  return accountHolderName;
 }
}

/**************** Bank.java ****************/
public class Bank {

 DBManager dbManager =new DBManager();
 
  
 public String processAccount(int accountID){
  
  //Some other code goes here
  
  String accountHolderName = dbManager.retrieveAccountHolderName(accountID);
  
  //some more processing code 
  
  return accountHolderName;
 }
}

/**************** BankTest.java ****************/
public class BankTest {

 @Test
 public void testBankProcessAccount() {

  new MockUp<DBManager>() {

   @SuppressWarnings("unused")
   @Mock
   public String retrieveAccountHolderName(int accountId ){
    return "Abhi";
   }
  };

  Bank bank = new Bank();
  
  String name = bank.processAccount(20);
  
  assertEquals("Account holder Name for A/C id 20 is 'Abhi' ","Abhi",name);

 }
}


How to mock private methods in JMockit?

Pretty cool, isn't it? To be able to modify private methods?

The follwing two examples will give you how a private method is redefined first by using the Expectations api and then the MockUp api.

First the Expectation API.  Yes of course, we can't call the private method directly in our Test class. BUT JMockit offers a neat Reflection Utility called the Deencapsulation class.

This class has static methods that can invoke private methods and access private fields.More about it later

The Expectations Way

/****************** Simple.java ***********/
public class Simple {


 private String iAmPrivate(){
  return "Private Method";
 }
 
 public String publicCallsPrivate(){
  return iAmPrivate();
 }
}

/**************** SimpleTest.java *********/
public class SimpleTest {

 @Test
 public void testPublicInvokesPrivate(){
  
  //Make simple final to be used in the Expectations inner class
  final Simple simple =  new Simple();
  
  //pass simple as argument to make it a Mocked type
  //in the Expectations class
  new Expectations(simple){
   {
    Deencapsulation.invoke(simple, "iAmPrivate");
    returns("I got INVOKED");
   }
  };
  
  String str = simple.publicCallsPrivate();
  assertEquals("The returned string is - I got INVOKED","I got INVOKED",str);
 }
 
}

Now for the MockUp way

/****************** Simple.java ***********/
public class Simple {


 private String iAmPrivate(){
  return "Private Method";
 }
 
 public String publicCallsPrivate(){
  return iAmPrivate();
 }
}

/**************** SimpleTest.java *********/
public class SimpleTest {
 
 @Test
 public void testPublicInvokesPrivateMockUp(){
  
  new MockUp<Simple>(){
   
   //Override the private method
   //Dont provide any ACCESSS MODIFIER!
   @Mock
   String iAmPrivate(){
    return "MockUp Invoke";
   }
   
  };
  
  Simple simple = new Simple();
  
  String str = simple.publicCallsPrivate();
  assertEquals("String returned - MockUp Invoke","MockUp Invoke",str);
  
 }
}


How to mock static methods in JMockit?

Again we will see how to mock a static method by using the Expectations and MockUp apis.

The Expectations Way

The only thing to be done is to mark the field in the test class with @Mocked or create a local variable in the anonymous Expectations class.

Lets go back to the Bank example. Our Bank class has a method called makeConection() that internally calls DBManager's getConnectionString() static method. However when we write the test method we want the getConnectionString() to return altogether a different String.


/********** DBManager.java **************/
public class DBManager {
 
 public static String getConnectionString(){
  return "ORIGINAL";
 }
}

/********** Bank.java **************/
public class Bank {
 
 public String makeConnection(){
  //some connection related code
  //goes here
  
  // call to static method
  String conStr = DBManager.getConnectionString();
  
  // If the connection String 
  // is anything other than
  // ORIGINAL return FAIL 
  if(conStr.equals("ORIGINAL"))
   return "SUCCESS";
  else
   return "FAIL";
 }
}

/********** BankTest.java **************/
public class BankTest {

 @Test
 public void testMakeConnection(){
  
  new NonStrictExpectations(){
   // DBManager is mocked here
   DBManager dbManager;
   
   {
    DBManager.getConnectionString();
    returns("DUPLICATE");
   }
  };
  
  Bank bank =  new Bank();
  String status = bank.makeConnection();
  
  assertEquals("Status is FAIL","FAIL",status);
 }
}


Now For the MockUp Way.....
/********** DBManager.java **************/
public class DBManager {
 
 public static String getConnectionString(){
  return "ORIGINAL";
 }
}

/********** Bank.java **************/
public class Bank {
 
 public String makeConnection(){
  //some connection related code
  //goes here
  
  // call to static method
  String conStr = DBManager.getConnectionString();
  
  // If the connection String 
  // is anything other than
  // ORIGINAL return FAIL 
  if(conStr.equals("ORIGINAL"))
   return "SUCCESS";
  else
   return "FAIL";
 }
}

/********** BankTest.java **************/
public class BankTest {

 @Test
 public void testMakeConnectionWithMockUp(){
  new MockUp<DBManager>(){
   
   // Redefine the method here
   // But With No static modifier
   @Mock
   public String getConnectionString(){
    return "DUPLICATE";
   }

  };
  
  Bank bank =  new Bank();
  String status = bank.makeConnection();
  
  
  assertEquals("Status is FAIL","FAIL",status);
 }
}

How to invoke or test private methods in JMockit?

If you have read the previous "How to"s, you already know the answer - its the Deencapsulation class.

There may be a private method you want to test. Generally if you read any article-they'll usually advise to move it to more broader visibility. I personally wont agree to this.

If you are stuck with writing test methods for legacy code, you cant just refactor the code and check it in. The latter is a mammoth process in itself.

Another option is to use the native reflection api's provided by java. I've tried this. It becomes a bowl of hot vegetable noodles you wont be able to handle later on.

Back to Deencapsulation class! The most used methods are invoke() , setField() and the getField().
The names are pretty intuitive and you'll be using them all over your junits when dealing with private variables and methods.


How to throw Exceptions in an Expectations block?

Till now we have been returning an object or some literal when we mock a method. What if we want to throw an Exception? There's a substitute for the returns() method we've been using in all the Expectations blocks till now.

We could have replaced it with something called result = [return object]. The following example shows where we throw an exception in the Expectations block.


/********** Bank.java **************/
public class Bank {
 
 public String getConnection() throws Exception{
  return "Connection";
  //some thing here might throw an exception
 }
} 

/********** BankTest.java **********/
public class BankTest {

 @Test(expected=Exception.class)
 public void testGetConnection() throws Exception{
  final Bank bank = new Bank();
  new Expectations(bank){
   {
    bank.getConnection();
    result = new Exception();
   }
  };
  bank.getConnection();
 }
}

Notice the use of the result = new Exception() in the Expectations block. In this I anticipated an Exception to be thrown and have stated that in the @Test annotation.
In the  MockUp api its straightforward, you just need to redefine the method to throw new Exception().


Some more JMockit utilities before signing off.

There's a lot of JMockit stuff to write about. Know the 80-20 rule? You'll be using only 20% of JMockit's features 80% of the time. All of the above is what I've been using most of the times.

These are some of the Home work you need to do.Learn it. If you got any doubts we'll discuss..
  1. Use Verifcations to verify method calls/ number of  after you've invoking the test method.
  2. Learn Reentrant mocks.
  3. Use @Cascading if there is a method chain to be tested.
  4.  Learn how to use the any fields - anyInt, any, anyString, anyLong...
  5. Check out setUpMock()
  6. Learn maxTimes and other similar fields.

25 comments:

  1. Nice work, just what I was looking for.

    ReplyDelete
    Replies
    1. One of the most beautiful things that can be experienced in life is when you return to your blog after a long time to view a comment!
      Thanks for the kind encouraging words! :)

      Delete
  2. Brilliant post. I am trying to learn JMockit and am getting very confused by the examples on the site. Thank you so much Abhinandan.

    ReplyDelete
    Replies
    1. Thank you robert!..... You made my day! :)

      Delete
  3. nice.. explained with simple examples

    ReplyDelete
  4. one query: You mentioned above that dbManager.retrieveAccountHolderName(10) MUST be called with value '10'. What happens when some other internal code calls the same method with some other value?
    How can we mock a method only for some specific parameter, but let it work as it for other parameters?

    ReplyDelete
    Replies
    1. Sorry for the delayed reply.. I am a little busy with my projects and work...

      Q1. What happens when some other internal code calls the same method with some other value?
      Ans. (I couldn't quite get the question.)
      It will throw an error because it is an 'Expectations' mockup. You need to use the 'NonStrictExpectations'. Its there in the same example.

      Q2. How can we mock a method only for some specific parameter, but let it work as it for other parameters?
      Ans. As far as i remember, you need to use the 'NonStrictExpectations' class instead of 'Expectations' class.

      Delete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Replies
    1. Welcome.. Thanks for coming to my blog!

      Delete
  7. After studying so many Jmokit Tutorials I found this one very Nice among all and easy to understand. Nicely Explained , Thanks

    ReplyDelete
    Replies
    1. Thanks Priyanka! Your words are encouraging!

      Delete
  8. You made JMockit easy for me. Thanks for all the effort you have put in writing this.

    ReplyDelete
    Replies
    1. Yep.. it was difficult for me too.. Thanks for your kind words :)

      Delete
  9. Very Nice post, easy to understand with simple examples, I appreciate if you explain how to test void methods like we use ArgumetCatptor and Verify in mockito

    ReplyDelete
    Replies
    1. mockito? I haven't tried it... Will check it out if time permits...
      Thanks for the suggestion and reading my blog :)

      Delete
  10. Very well explained tutorial with sound full examples.

    ReplyDelete
  11. Excellent work. Easily the best JMockit tutorial out there.

    ReplyDelete
  12. Well explained with examples. Thanks for the tutorial.

    ReplyDelete
  13. OMG, Thank you, I'm researching mock tools and I've read just about every book, blog, article... and I came across your blog, it is concise and straight to the point. Big Thanks.

    ReplyDelete
  14. Tutoring is very important for students in SAT or any tests through this they can prepare tests With full
    sat prep boca raton confidence. And success ratio of your child can increase. It is very difficult for the choice of online tutoring. I like all this discussion.

    ReplyDelete