EasyMock
Testing Mediator Objects
copyright 2008 trainologic LTD
EasyMock
• Mediator Objects and Testing
• Introduction to Mock Objects
• Introduction to EasyMock
• Setting Expectations
• Mock Verification
copyright 2008 trainologic LTD
EasyMock
Testing Mediator Objects
• The JUnit testing framework, makes the testing of an
independent, non-void method, simple.
• But... how do we test an method with no return value?
• How do we test a method that interacts with other
domain objects?
3
copyright 2008 trainologic LTD
EasyMock
Testing Mediator Objects
• Let’s say we want to test a method that gets a
Collection and an Object, checks whether the Collection
contains the Object and only if it doesn’t, adds the
Object to the Collection.
• How would you implement this test?
4
copyright 2008 trainologic LTD
EasyMock
Testing Mediator Objects
• What if the method only gets a simple collection and
print it using a Logger?
5
copyright 2008 trainologic LTD
EasyMock
Testing Mediator Objects
• Many business objects that are really important to test
are mediators, i.e., they contain methods which
interact with other domain objects.
• Since in unit testing we want to test a unit without its
dependencies, we will have to eliminate them
somehow.
• We do this by passing dummy objects to “fill in” for the
real dependencies.
6
copyright 2008 trainologic LTD
EasyMock
Testing Mediator Objects
• These dummy objects are called Mock Objects.
• Which Mock Objects do we need for the add to
collection example?
• Which Mock Objects do we need for the print collection
example?
7
copyright 2008 trainologic LTD
EasyMock
• Mediator Objects and Testing
• Introduction to Mock Objects
• Introduction to EasyMock
• Setting Expectations
• Mock Verification
copyright 2008 trainologic LTD
EasyMock
Introduction to Mock Objects
• So... How “dumb” is a mock?
• First, it has to implement the same interface as the
original dependent object. Otherwise, the compiler
won’t let us pass it to the tested object.
• Second, it should supply the tested code with
everything it expects from the dependent object.
• Third, it should be able to check that the tested code is
using the dependent object (the Mock) correctly.
9
copyright 2008 trainologic LTD
EasyMock
Introduction to Mock Objects
• To use a Mock object we should:
• Create instance of the Mock.
• Set state and expectations.
• Invoke the tested code while passing the Mock as
parameter.
• Verify the Mock has been used correctly.
• Let’s see an example...
10
copyright 2008 trainologic LTD
EasyMock
Introduction to Mock Objects
• Let’s say we want to implement a special collection that
informs its listeners when and element is added to the
collection.
• That design is following the GoF Observer design
pattern.
11
copyright 2008 trainologic LTD
EasyMock
Introduction to Mock Objects
• To test this behavior we have to write the following
test:
@Test
public void testObservableCollection() {
Object objToAdd = new Object();
listenerMock.expect(1);
collection.addListener(listenerMock);
collection.add(objToAdd);
listenerMock.verify();
}
12
copyright 2008 trainologic LTD
EasyMock
Introduction to Mock Objects
• Let’s take a look at ListenerMock:
public class ListenerMock implements Listener {
private int counter;
private int expectedCounter;
public void elementAdded(){
counter++;
}
public void expect(int numOfCalls) {
expectedCounter = numOfCalls;
}
public void verify() {
if (counter != expectedCounter)
throw new AssertionFailedError();
}
}
13
copyright 2008 trainologic LTD
EasyMock
Introduction to Mock Objects
• As you can see, even though this is a very simple Mock,
it is cumbersome to write.
• It would have been much less pleasant if we had a
more complicated interface or if we had to check for
parameter values, return values and the order of
method calls.
14
copyright 2008 trainologic LTD
EasyMock
• Mediator Objects and Testing
• Introduction to Mock Objects
• Introduction to EasyMock
• Setting Expectations
• Mock Verification
copyright 2008 trainologic LTD
EasyMock
Introduction to EasyMock
• An open-source project providing an easy way to work
with Mock objects.
• Can be freely downloaded from easymock.org.
• Released under the MIT License.
16
copyright 2008 trainologic LTD
EasyMock
EasyMock - Benefits
• No need to write Mock objects by hand.
• Refactoring-safe, no need to refactor Mock in case the
interface has changed.
• Supports return values and Exceptions.
• Supports checking the order of calls for a single object
and for multiple projects.
• Uses a convenient record/replay mechanism.
17
copyright 2008 trainologic LTD
EasyMock
EasyMock - Installation
• Download EasyMock.
• Unzip the package.
• Add easymock.jar to your classpath.
• That is it!!!
18
copyright 2008 trainologic LTD
EasyMock
EasyMock
Create a Mock
19
copyright 2008 trainologic LTD
EasyMock
EasyMock
Create a Mock
Set Expectations
19
copyright 2008 trainologic LTD
EasyMock
EasyMock
Create a Mock
Set Expectations
Run Test
19
copyright 2008 trainologic LTD
EasyMock
EasyMock
Create a Mock
Set Expectations
Run Test
Verify
19
copyright 2008 trainologic LTD
EasyMock
EasyMock
• Let’s try to implement the same ListenerMock using
EasyMock.
@Test
public void testObservableCollection() {
mockListener = EasyMock.createMock(Listener.class);
//Recording
mockListener.elementAdded();
EasyMock.replay(mockListener);
//Playing
Object objToAdd = new Object();
collection.addListener(mockListener);
collection.add(objToAdd);
//Verifying
EasyMock.verify(mockListener);
}
20
copyright 2008 trainologic LTD
EasyMock
EasyMock
• No need to implement the Listener interface. The
implementation is being created by EasyMock on the
fly.
• After the creation of the Mock, EasyMock is in recording
mode.
• In the recording mode, we invoke methods of the Mock
and these method calls are recorded and used as
expectations.
21
copyright 2008 trainologic LTD
EasyMock
EasyMock
• The replay() method changes the mode from recording
to playing.
• Then the actual test is run and the Mock is being used
by the tested code.
• Finally, the verify() method makes sure the play was
identical to the recording.
22
copyright 2008 trainologic LTD
EasyMock
Creating Mock
• Creating a Mock is very easy... All we have to do is just
use the createMock() method and supply it with the
interface to implement.
Listener listenerMock = EasyMock.createMock(Listener.class);
23
copyright 2008 trainologic LTD
EasyMock
Creating Mock
• What happens if I don’t have an interface?
• Well... There are two options:
• Extract an interface;
• Use EasyMock Extension that works with classes as
well (separate download). The extension uses cglib to
dynamically create a sub-class.
• The next part of the chapter discus the expectation
settings and verification.
24
copyright 2008 trainologic LTD
EasyMock
• Mediator Objects and Testing
• Introduction to Mock Objects
• Introduction to EasyMock
• Setting Expectations
• Mock Verification
copyright 2008 trainologic LTD
EasyMock
Setting Expectations
• Setting expectations is mostly done by recording.
• But not everything can be recorded... A complementary
API enables us to refine our expectations.
• In the following example the Mock expects one call to
elementAdded() method.
listenerMock = EasyMock.createMock(Listener.class);
//Recording
listenerMock.elementAdded();
EasyMock.replay(listenerMock);
26
copyright 2008 trainologic LTD
EasyMock
Setting Expectations - Argument
Values
• In case there is a method argument, the Mock expects
an argument that equals() the argument in the
recording.
listenerMock = EasyMock.createMock(Listener.class);
//Recording
listenerMock.elementAdded(element);
EasyMock.replay(listenerMock);
27
copyright 2008 trainologic LTD
EasyMock
Setting Expectations - Number of
Calls
• If you want to expect more than one call, you can
either call the method several times in the recording
phase, or use the times() method.
listenerMock = EasyMock.createMock(Listener.class);
//Recording
listenerMock.elementAdded(element);
EasyMock.expectLastCall().times(3);
EasyMock.replay(listenerMock);
28
copyright 2008 trainologic LTD
EasyMock
Setting Expectations - Specifying
Return Values
• To specify what value is returned from a method call on
the Mock, use the expect(...).andReturn()
• During the playing phase, a call to getCallCount()
method will return 3.
listenerMock = EasyMock.createMock(Listener.class);
//Recording
EasyMock.expect(listenerMock.getCallCount()).andReturn(3);
EasyMock.replay(listenerMock);
29
copyright 2008 trainologic LTD
EasyMock
Setting Expectations - Specifying
Exception Throwing
• To specify that a method call on the Mock throws and
Exception, use the andThrow().
• During the playing phase, a call to getCallCount() will
throw a RuntimeException
listenerMock = EasyMock.createMock(Listener.class);
//Recording
listenerMock.getCallCount();
EasyMock.expectLastCall().andThrow(new RuntimeExcetion());
EasyMock.replay(listenerMock);
30
copyright 2008 trainologic LTD
EasyMock
Setting Expectations -
Combination
• We can combine all the options:
listenerMock = EasyMock.createMock(Listener.class);
//Recording
listenerMock.getCallCount();
EasyMock.expectLastCall()
.andReturn(3)
.times(2)
.andThrow(new RuntimeExcetion());
EasyMock.replay(listenerMock);
31
copyright 2008 trainologic LTD
EasyMock
Setting Expectations - Call Count
• By default, EasyMock fails if the count of the calls does
not match the exact number of call set in the recording.
• To change this behavior use times(), atLeastOnce() or
anyTimes()
listenerMock = EasyMock.createMock(Listener.class);
//Recording
listenerMock.getCallCount();
EasyMock.expectLastCall().times(2, 5); //At least 2 Max 5
EasyMock.expectLastCall().atLeastOnce(); //one or more
EasyMock.expectLastCall().anyTimes(); //zero or more
EasyMock.replay(listenerMock);
32
copyright 2008 trainologic LTD
EasyMock
Setting Expectations - Invocation
Order
• By default, EasyMock doesn’t check the order of
method calls.
• To change this behavior, you have to create a
StrictMock.
• We do that by calling the createStrictMock() method.
• StrictMocks check invocation orders on a single Mock.
listenerMock = EasyMock.createStrictMock(Listener.class);
33
copyright 2008 trainologic LTD
EasyMock
Setting Expectations - Invocation
Order
• If you want to verify invocation order that spans
several Mocks, you have to create a StrictControl and
use it to create the Mocks.
strictControl = EasyMock.createStrictControl();
listenerMock = strictControl.createMock(Listener.class);
34
copyright 2008 trainologic LTD
EasyMock
Setting Expectations - Argument
Matchers
• EasyMock expects arguments passed during the play
phase to be equals() to arguments being recorded.
• To have better control over argument values matching,
we use matchers.
• Matchers are simple methods that surround the
arguments during recording phase.
listenerMock = EasyMock.createMock(Listener.class);
//Recording
listenerMock.elementAdded(EasyMock.anyObject(element));
EasyMock.replay(listenerMock);
35
copyright 2008 trainologic LTD
EasyMock
Setting Expectations - Argument
Matchers
• There is a long list of available Matchers.
• All of them have self-descriptive names.
• In case you use matcher on one argument you must
use matchers on all arguments.
36
copyright 2008 trainologic LTD
EasyMock
Setting Expectations - Nice Mock
• If we have no expectations from the Mock and we only
want it to fill-in for the missing object and return
default values, we can create a “Nice Mock”.
• A nice Mock is a Mock which doesn’t care how we use
it.
• To create it, we simply call createNiceMock().
listenerMock = EasyMock.createNiceMock(Listener.class);
37
copyright 2008 trainologic LTD
EasyMock
• Mediator Objects and Testing
• Introduction to Mock Objects
• Introduction to EasyMock
• Setting Expectations
• Mock Verification
copyright 2008 trainologic LTD
EasyMock
Moving to Play Phase
• After setting all expectations, we change to play mode
and run our test code.
• Moving to play phase is done by calling the replay()
method and pass all Mocks as arguments.
EasyMock.replay(listenerMock);
39
copyright 2008 trainologic LTD
EasyMock
Moving to Play Phase
• During the play phase, EasyMock is tracking method
invocations on the Mocks.
• Even in case the invocations do not match the
expectations, EasyMock doesn’t fail the tests yet...
• There is still one more stage to go through...
Verification Stage.
40
copyright 2008 trainologic LTD
EasyMock
Verification
• After all test code finished executing, we want to check
that the Mock was used as we expected.
• To check this condition we simply call the verify()
method and pass all Mocks as arguments.
• In case one of our expectations is not fulfilled, an
exception is thrown and fails the test.
41
copyright 2008 trainologic LTD
EasyMock
Summary
• In order to unit-test isolated units we must use Mock
objects.
• Writing Mocks by hand is cumbersome.
• EasyMock is a simple and easy to use library that helps
us create Mock implementations on the fly.
• Expectation setting in EasyMock is done by applying
record/replay phases which provides great flexibility.
42
copyright 2008 trainologic LTD