Sie sind auf Seite 1von 16

JMockit :

2.1. Introduction
First of all, let's talk about what JMockit is: a Java framework for mocking objects in tests
(you can use it for both JUnit and TestNG ones).
It uses Java's instrumentation APIs to modify the classes' bytecode during runtime in order
to dynamically alter their behavior. Some of its strong points are its expressibility and its
out-of-the-box ability to mock static and private methods.
Maybe you're new to JMockit, but it's definitely not due to it being new. JMockit's
development started in June 2006 and its first stable release dates to December 2012, so it's
been around for a time now (current version is 1.24 at the time of writing the article).

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

 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

2.2. The Expressibility of JMockit


As told before, one of the strongest points of JMockit is its expressibility. In order to create
mocks and define their behavior, instead of calling methods from the mocking API, you just
need to define them directly.
This means that you won't do things like:
1 API.expect(mockInstance.method()).andThenReturn(value).times(2);
Instead, expect things like:
1 new Expectation() {
2 mockInstance.method();
3 result = value;
4 times = 2;
}
5
It might seem that it is more code, but you could simply put all three lines just on one. The
really important part is that you don't end up with a big “train” of chained method calls.
Instead, you end up with a definition of how you want the mock to behave when called.
If you take into account that on the result = value part you could return anything (fixed
values, dynamically generated values, exceptions, etc), the expressiveness of JMockit gets
even more evident.

2.3. The Record-Replay-Verify Model


Tests using JMockit are divided into three differentiated stages: record, replay and verify.

1. On the record phase, during test preparation and before the invocations to the
methods we want to be executed, we will define the expected behavior for all tests
to be used during the next stage.
2. The replay phase is the one in which the code under test is executed. The invocations
of mocked methods/constructors previously recorded on the previous stage will now
be replayed.
3. Lastly, on the verify phase, we will assert that the result of the test was the one we
expected (and that mocks behaved and were used according to what was defined in
the record phase).

With a code example, a wireframe for a test would look something like this:
1
2 @Test
3 public void testWireframe() {
// preparation code not specific to JMockit, if any
4
5 new Expectations() {{
6 // define expected behaviour for mocks
7 }};
8
9 // execute code-under-test
10
new Verifications() {{
11
// verify mocks
12 }};
13
14 // assertions
15 }
16

3. Creating Mocks

3.1. JMockit's Annotations


When using JMockit, the easiest way to use mocks, is to use annotations. There are three
for creating mocks (@Mocked, @Injectable and @Capturing) and one to specify the class
under testing (@Tested).
When using the @Mocked annotation on a field, it will create mocked instances of each and
every new object of that particular class.
On the other hand, with the @Injectable annotation, only one mocked instance will be
created.
The last annotation, @Capturing will behave like @Mocked, but will extend its reach to
every subclass extending or implementing the annotated field's type.

Basic difference between @Mocked and @Injectable?

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.

3.2. Passing Arguments to Tests


When using JMockit is possible to pass mocks as test parameters. This is quite useful for
creating a mock just for that one test in particular, like some complex model object that
needs a specific behavior just for one test for instance. It would be something like this:
1
2 @RunWith(JMockit.class)
3 public class TestPassingArguments {
4
5 @Injectable
6 private Foo mockForEveryTest;
7
@Tested
8 private Bar bar;
9
10 @Test
11 public void testExample(@Mocked Xyz mockForJustThisTest) {
12 new Expectations() {{
mockForEveryTest.someMethod("foo");
13 mockForJustThisTest.someOtherMethod();
14 }};
15
16 bar.codeUnderTest();
17 }
18 }
19
This way of creating a mock by passing it as a parameter, instead of having to call some API
method, again shows us the expressibility we're talking about since the beginning.

3.3. Complete Example


To end this article, we'll be including a complete example of a test using JMockit.
In this example, we'll be testing a Performer class that uses Collaborator in
its perform() method. This perform() method, receives a Model object as a parameter from
which it will use its getInfo() that returns a String, this String will be passed to
the collaborate() method from Collaborator that will return true for this particular test, and
this value will be passed to the receive() method from Collaborator.
So, the tested classes will look like this:
1
2
3 public class Model {
public String getInfo(){
4 return "info";
5 }
6 }
7
8 public class Collaborator {
public boolean collaborate(String string){
9 return false;
10 }
11 public void receive(boolean bool){
12 // NOOP
13 }
}
14
15 public class Performer {
16 private Collaborator collaborator;
17
18 public void perform(Model model) {
19 boolean value = collaborator.collaborate(model.getInfo());
collaborator.receive(value);
20
}
21 }
22
23
And the test's code will end up being like:
1 @RunWith(JMockit.class)
public class PerformerTest {
2
3 @Injectable
4 private Collaborator collaborator;
5
6 @Tested
7 private Performer performer;
8
9 @Test
public void testThePerformMethod(@Mocked Model model) {
10 new Expectations() {{
11 model.getInfo();result = "bar";
12 collaborator.collaborate("bar"); result = true;
13 }};
performer.perform(model);
14 new Verifications() {{
15 collaborator.receive(true);
16 }};
17 }
}
18

JMockit Tutorial : Learn it today with examples

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 ****************/
1
public class Person {
2
3 private String name;
4
5
6 public Person(){
7 name = "Default Abhi";
8 }
9
public String getName() {
10
return name;
11 }
12
13 public void setName(String name) {
14 this.name = name;
}
15 }
16
17 /******************* PersonTestConstructor.java ***********/
18 public class PersonTestConstructor {
19
20 @Test
21 public void testGetName() {
new MockUp<Person>() {
22 @Mock
23 public void $init() {
24 //Dont assign name variable at all
25 //Leave it null
26 }
27
};
28
29 Person p = new Person();
30 String name = p.getName();
31
32 assertNull("Name of person is null",name);
33 }
34
35 }

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.
?
1 /************************* Person.java ****************/
2 public class Person {
3
4 private String name;
5
public Person(String name){
6 this.name = name;
7 }
8
9 public String getName() {
10 return name;
11 }
12
13 public void setName(String name) {
this.name = name;
14 }
15 }
16
17 /******************* PersonTestConstructor.java ***********/
18 public class PersonTestConstructor {
19
20 @Test
public void testGetName() {
21 new MockUp<Person>() {
22 @Mock
23 public void $init(String name) {
24 //Dont assign name variable at all
25 //Leave it null
}
26
27 };
28
29 Person p = new Person("AbhiJMockit");
30 String name = p.getName();
31 System.out.println(name);
32 assertNull("Name of person is null",name);
}
33
34 }
35

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.

?
1 /***************** Bank.java ******************/
2 public class Bank {
3
static int balanceAmount;
4
5 //Static block begins
6 static {
7 updateBalance(100);
8 }
9
10 public static void updateBalance(float balance) {
balanceAmount += balance;
11 }
12 }
13
14 /*********************** BankTest.java **********/
15 public class BankTest {
16
17 @Test
public void testBankStaticBlock(){
18
19 new MockUp<Bank>(){
20
21 @SuppressWarnings("unused")
22 @Mock
23 public void $clinit(){
24 Bank.updateBalance(500);
}
25 };
26
27 assertEquals("The balance amount is 500", 500, Bank.balanceAmount);
28
29 }
30 }
31

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 *************/
1
public class DBManager {
2
3 public String retrieveAccountHolderName(int accountId ){
4 String accountHolderName = null;
5
6 //connect to db
7 //retrieve the Account Holder Name
8
return accountHolderName;
9
}
10 }
11
12 /**************** Bank.java ****************/
13 public class Bank {
14
15 DBManager dbManager =new DBManager();
16
17
public String processAccount(int accountID){
18
19
//Some other code goes here
20
21 String accountHolderName = dbManager.retrieveAccountHolderName(accountID);
22
23 //some more processing code
24
25 return accountHolderName;
26 }
}
27
28
/*************** BankTest.java ****************/
29
30 public class BankTest {
31
32 @Test
33 public void testRetrieveAccountHolderName() {
34
35 Bank bank = new Bank();
36
// Define the Expectations block here
37
new Expectations() {
38
39 DBManager dbManager; // variables declared here are mocked by default
40
41 {
42 dbManager.retrieveAccountHolderName(10);
43 returns("Abhi");
}
44
};
45
46 String name = bank.processAccount(10);
47
48 assertEquals("Account holder Name for A/C id 10 is 'Abhi' ","Abhi",name);
49
50 }
}
51
52

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.

?
1 /**************** DBManager.java *************/
2 public class DBManager {
3
4 public String retrieveAccountHolderName(int accountId ){
String accountHolderName = null;
5
6 //connect to db
7 //retrieve the Account Holder Name
8
9 return accountHolderName;
10 }
11 }
12
/**************** Bank.java ****************/
13 public class Bank {
14
15 DBManager dbManager =new DBManager();
16
17
18 public String processAccount(int accountID){
19
20 //Some other code goes here
21
22 String accountHolderName = dbManager.retrieveAccountHolderName(accountID);
23
//some more processing code
24
25 return accountHolderName;
26 }
27 }
28
29 /**************** BankTest.java ****************/
30 public class BankTest {
31
@Test
32 public void testBankProcessAccount() {
33
34 new MockUp<DBManager>() {
35
36 @SuppressWarnings("unused")
37 @Mock
38 public String retrieveAccountHolderName(int accountId ){
return "Abhi";
39 }
40 };
41
42 Bank bank = new Bank();
43
44 String name = bank.processAccount(20);
45
46 assertEquals("Account holder Name for A/C id 20 is 'Abhi' ","Abhi",name);
47
}
48 }
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

?
1 /****************** Simple.java ***********/
2 public class Simple {
3
4
5 private String iAmPrivate(){
return "Private Method";
6
}
7
8 public String publicCallsPrivate(){
9 return iAmPrivate();
10 }
11 }
12
/**************** SimpleTest.java *********/
13
public class SimpleTest {
14
15 @Test
16 public void testPublicInvokesPrivate(){
17
18 //Make simple final to be used in the Expectations inner class
19 final Simple simple = new Simple();
20
//pass simple as argument to make it a Mocked type
21
//in the Expectations class
22 new Expectations(simple){
23 {
24 Deencapsulation.invoke(simple, "iAmPrivate");
25 returns("I got INVOKED");
}
26
};
27
28 String str = simple.publicCallsPrivate();
29 assertEquals("The returned string is - I got INVOKED","I got INVOKED",str);
30 }
31
32 }
Now for the MockUp way

?
1
2
/****************** Simple.java ***********/
3 public class Simple {
4
5
6 private String iAmPrivate(){
7 return "Private Method";
8 }
9
10 public String publicCallsPrivate(){
return iAmPrivate();
11 }
12 }
13
14 /**************** SimpleTest.java *********/
15 public class SimpleTest {
16
17 @Test
public void testPublicInvokesPrivateMockUp(){
18
19 new MockUp<Simple>(){
20
21 //Override the private method
22 //Dont provide any ACCESSS MODIFIER!
23 @Mock
24 String iAmPrivate(){
return "MockUp Invoke";
25 }
26
27 };
28
29 Simple simple = new Simple();
30
31 String str = simple.publicCallsPrivate();
32 assertEquals("String returned - MockUp Invoke","MockUp Invoke",str);
33
}
34 }

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.

?
1 /********** DBManager.java **************/
2 public class DBManager {
3
public static String getConnectionString(){
4 return "ORIGINAL";
5 }
6 }
7
8 /********** Bank.java **************/
9 public class Bank {
10
public String makeConnection(){
11 //some connection related code
12 //goes here
13
14 // call to static method
15 String conStr = DBManager.getConnectionString();
16
17 // If the connection String
// is anything other than
18 // ORIGINAL return FAIL
19 if(conStr.equals("ORIGINAL"))
20 return "SUCCESS";
21 else
22 return "FAIL";
}
23 }
24
25 /********** BankTest.java **************/
26 public class BankTest {
27
28 @Test
29 public void testMakeConnection(){
30
new NonStrictExpectations(){
31 // DBManager is mocked here
32 DBManager dbManager;
33
34 {
35 DBManager.getConnectionString();
36 returns("DUPLICATE");
}
37 };
38
39 Bank bank = new Bank();
40 String status = bank.makeConnection();
41
42 assertEquals("Status is FAIL","FAIL",status);
43 }
}
44
45

Now For the MockUp Way.....


?
1 /********** DBManager.java **************/
2 public class DBManager {
3
4 public static String getConnectionString(){
5 return "ORIGINAL";
}
6 }
7
8 /********** Bank.java **************/
9 public class Bank {
10
11 public String makeConnection(){
12 //some connection related code
//goes here
13
14 // call to static method
15 String conStr = DBManager.getConnectionString();
16
17 // If the connection String
18 // is anything other than
19 // ORIGINAL return FAIL
if(conStr.equals("ORIGINAL"))
20 return "SUCCESS";
21 else
22 return "FAIL";
23 }
24 }
25
/********** BankTest.java **************/
26 public class BankTest {
27
28 @Test
29 public void testMakeConnectionWithMockUp(){
30 new MockUp<DBManager>(){
31
32 // Redefine the method here
// But With No static modifier
33 @Mock
34 public String getConnectionString(){
35 return "DUPLICATE";
36 }
37
38 };
39
40 Bank bank = new Bank();
41 String status = bank.makeConnection();
42
43
assertEquals("Status is FAIL","FAIL",status);
44 }
45 }
46

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.

Das könnte Ihnen auch gefallen