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.
Remember the to put the @Mock annotation above. I have spent hours trying to debug only to find out that I forgot.
Again we use $init to mock the constructor but this time with parameters.
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.
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.
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.
- What is Mocking in unit Testing?
- How does JMockit support mocking?
- How to add JMockit to an eclipse project?
- How to run a test case with JMockit?
- How to mock default constructors(or constructors with no parameters) in JMockit?
- How to mock constructors with parameters (or those that take arguments) ?
- How to mock static initialization blocks in JMockit?
- How to mock public methods in JMockit
- How to mock private methods in JMockit?
- How to mock static methods in JMockit?
- How to invoke or test private methods in JMockit?
- How to throw exceptions in an Expectations block?
- 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) ?
/************************* 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?
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.
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...
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 :
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.
/**************** 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 :
- Only retrieveAccountHolderName() method has to be called on the dbManager object in the following code.
- AND the argument MUST be 10. Anything other that 10 will throw the same UnexpectedInvocation Exception. Try this yourself.
- 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
Now for the MockUp way
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.
Now For the MockUp Way.....
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.
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.
In the MockUp api its straightforward, you just need to redefine the method to throw new Exception().
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..
These are some of the Home work you need to do.Learn it. If you got any doubts we'll discuss..
- Use Verifcations to verify method calls/ number of after you've invoking the test method.
- Learn Reentrant mocks.
- Use @Cascading if there is a method chain to be tested.
- Learn how to use the any fields - anyInt, any, anyString, anyLong...
- Check out setUpMock()
- Learn maxTimes and other similar fields.
Nice work, just what I was looking for.
ReplyDeleteOne 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!
DeleteThanks for the kind encouraging words! :)
Brilliant post. I am trying to learn JMockit and am getting very confused by the examples on the site. Thank you so much Abhinandan.
ReplyDeleteThank you robert!..... You made my day! :)
Deletenice.. explained with simple examples
ReplyDeleteThank you! sidggate!
Deleteone 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?
ReplyDeleteHow can we mock a method only for some specific parameter, but let it work as it for other parameters?
Sorry for the delayed reply.. I am a little busy with my projects and work...
DeleteQ1. 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.
This comment has been removed by the author.
ReplyDeleteThanks, nice post
ReplyDeleteWelcome.. Thanks for coming to my blog!
DeleteAfter studying so many Jmokit Tutorials I found this one very Nice among all and easy to understand. Nicely Explained , Thanks
ReplyDeleteThanks Priyanka! Your words are encouraging!
DeleteYou made JMockit easy for me. Thanks for all the effort you have put in writing this.
ReplyDeleteYep.. it was difficult for me too.. Thanks for your kind words :)
DeleteVery 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
ReplyDeletemockito? I haven't tried it... Will check it out if time permits...
DeleteThanks for the suggestion and reading my blog :)
Very well explained tutorial with sound full examples.
ReplyDeleteThanks a lot Ashok!
DeleteExcellent work. Easily the best JMockit tutorial out there.
ReplyDeleteAt your service, Mr.President!
DeleteWell explained with examples. Thanks for the tutorial.
ReplyDeletewelcome! :D
DeleteOMG, 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.
ReplyDeleteHi Nandu, Your JMockit example are simple and easy to understand. I felt your blog made me to understand JMockit easily. Thanks Dude. I appreciate your hard work to create this tutorial. If you get time you can create some complex examples.
ReplyDeleteAwesome Abhi.....Very Nice Tutorial
ReplyDeleteThank you for taking out the time to write this tutorial.
ReplyDeleteIrony that the site of JMockit itself is awful in terms of explaining their own lib.
very neat and precise!!
ReplyDeleteThank a lot
ReplyDeletehttp://www.catchmycity.com/tutorial/programming-language/unit-test-framework?Framework=JMockit
Very nice tutorial, with clear structure and easy to follow. Great! One thing, the links going off the main page seem broken would you mind checking?
ReplyDeleteis it? I did not get which is the main page.
DeletePlease send met he main page link.
Yes, som of the links dont work http://jmockit.googlecode.com/svn/trunk/www/tutorial/BehaviorBasedTesting.html#verification
DeleteVery nice tutorial, thanks!
ReplyDeleteI tried to follow your tutorial about mocking static methods but it seems that dbManager is not mocked.
ReplyDeletenew NonStrictExpectations() {
// DBManager is mocked here
DBManager dbManager;
{
DBManager.getConnectionString();
returns("DUPLICATE");
}
};
I need to change it as below and it works:
new NonStrictExpectations(DBManager.class){ // Partial mock for DBManager
{
DBManager.getConnectionString();
returns("DUPLICATE");
}
};
Is my correction correct or it should be done another way?
Thanks,
The tutorial is very nice & helped me to learn things faster. Thanks a lot...:)
ReplyDeletecan you show how you would mock the constructor with a new initialised value?
ReplyDeleteSo much better then the tutorial on http://jmockit.org/ Thanks!
ReplyDeleteCould you explain the principle of Jmockit.
ReplyDeleteFor example, how does Jmokit record expectations ?
I am hoping for your reply.
ReplyDeleteCould you reply me by email. mine is hzdavid2009@gmail.com
thank you very much.
I am very likely to know the principle of Jmockit.
It is a legend tool.
Could you explain the principle of Jmockit.
ReplyDeleteFor example, how does Jmokit record expectations ?
Any idea why I'm getting this exception while running above program ?
ReplyDeletejava.lang.IllegalArgumentException: Invalid mock method a.b.c.TestAClass$1#methodToMock(int) for private method of internal class
Abhi, dhanyavaad!
ReplyDeleteReal good stuff...
Thanks for keeping the post simple :)
ReplyDeleteGreat job. As easy as it can get to learn
ReplyDeleteSK Pahari service provided mockup design, mockup maker, mockup in delhi, mockup making in india. VIEW MORE :- Mockup Design
ReplyDeleteHelp full blog . You saved a lot of time.Example are very nice to understand even for beginners . Thank you !!!
ReplyDeleteGreat blog, I was going through various ppts and blogs. But this one explained the concepts in a pragmatic way. You saved my day :-)
ReplyDeleteShri Ram Packaging Industry is one of the Leading Manufactures & suppliers of International standard pinewood pallets, Euro Pallets, ISPM-15 Heat Treated wooden Pallets, Crates, boxes since last 4 decade in Gurgaon, Bhiwadi.
ReplyDeleteVIEW MORE
Paper Angle Board Manufacturer in Gurugram
Thankyou..
Shri Ram Packaging Industry is one of the Leading Manufactures & suppliers of International standard pinewood pallets, Euro Pallets, ISPM-15 Heat Treated wooden Pallets, Crates, boxes since last 4 decade in Gurgaon, Bhiwadi.
ReplyDeleteVIEW MORE
Paper Angle Board Manufacturer in Gurugram
Thankyou..
Awasome blog..Thanks for sharing
ReplyDeleteThe auto parts industry silencer manufacturer, Two wheeler silencer manufacturers india, two wheeler silencer manufacturer, two wheeler silencer makers..
VIEW MORE
dolphin silencer manufacturers in Delhi
Thankyou..
This is a great blog but I note that it is not based on the latest version, for example Deencapsulation.invoke was deprecated in Version 1.35 (Sep 24, 2017) and the latest version is 1.43 which uses a different approach.
ReplyDeletenew MockUp(){
ReplyDelete//Override the private method
//Dont provide any ACCESSS MODIFIER!
@Mock
String iAmPrivate(){
return "MockUp Invoke";
}
}; didnt work with 1.48 , any idea how to fix it
This comment has been removed by a blog administrator.
ReplyDelete