Thursday, October 22, 2009

EASYMOCK









































Prev don't be afraid of buying books Next






























EASYMOCK



EasyMock [URL 25] is a framework for creating
mock objects dynamically at runtime. It was written and is
maintained by Tammo Freese, who presented it in a paper at
XP2002[18].



Using EasyMock does, indeed, make things easier.
We take the following test case from the MockMaker example in the
previous section and rewrite it to use EasyMock. Then we'll look at
how EasyMock is used in the example as well as some of its other
capabilities.




public class TestEasyMockXmlCalculator extends TestCase {
private MockControl control;
private IntCalculator mockCalculator;

protected void setUp() {
control = EasyMock.controlFor(IntCalculator.class);
mockCalculator = (IntCalculator)control.getMock();
}

public void testSimpleAdd() throws CalculatorException
{

mockCalculator.calculate(1, 1, "/add");
control.setReturnValue(2);
control.activate();
XmlCalculator xmlCalculator = new XmlCalculator(mockCalculator);
String xmlString = "<calculate operation=\"/add\">"
+ " <argument>1</argument>"
+ " <argument>1</argument>"
+ "</calculate>";
assertEquals("Bad simple add result",2,xmlCalculator.calculate(xmlString));
control.verify();
}






public void testComplexAdd() throws CalculatorException {
mockCalculator.calculate(1, 1, "/add");
control.setReturnValue(2);
mockCalculator.calculate(2, 2, "/add");
control.setReturnValue(4);

control.activate();

XmlCalculator xmlCalculator = new XmlCalculator(mockCalculator);
String xmlString = "<calculate operation=\"/add\">"
+ " <argument>"
+ " <calculate operation=\"/add\">"
+ " <argument>1</argument>"
+ " <argument>1</argument>"
+ " </calculate>"
+ " </argument>"
+ " <argument>2</argument>"
+ "</calculate>";
assertEquals("Bad nested add result",4,xmlCalculator.calculate(xmlString));
control.verify();
}
}








Basic Features



When creating a mock with EasyMock, you start by
creating an instance of MockControl for the interface you
want to mock. This is done with a call to a static factory method
in the EasyMock class:




MockControl control = EasyMock.controlFor(IntCalculator.class);






The next step is to ask the control for a
mock:




IntCalculator mockCalculator = (IntCalculator)control.getMock();






Now we can specify our expectations by using the
newly created mock and the control. You set method call
expectations by making the expected method call on the mock, with
the expected arguments. For example:




mockCalculator.calculate(1, 1, "/add");






We can specify the result to be returned from
this method call by using the control:




control.setReturnValue(2);







That's pretty much it. We continue this
until our expectations have been specified. Then we make one more
call to the control to switch the mock from specification mode to
usage mode:




control.activate();






We then use the mock in our test as usual. At
the end we ask the control to verify:




control.verify();





















Remember that we need to ask the control to
verify the expectations, not the mock. This is a significant
difference from working with hand-coded or MockMaker-generated
mocks. Be sure to keep it in mind, especially if you're working
with a mixture of the two.
















That's the basic idea. There are some additional
capabilities we can take advantage of. If we set an expectation
that a void method is to be called, then we can skip the
step of setting the return value and it will be assumed to be void.
If, however, we prefer to make that explicit, we can do so with the
following instead of calling setReturnValue():




control.setVoidCallable();








Advanced Features



So far, all of the expectations we've looked at
simply specify the methods to be called, the expected arguments,
and the value to return (or none if it's void). There are
no expectations about how many times the method should be called.
The only thing we set an expectation for is that it will be called. We can set an
expectation about how many times a method is to be called by
passing that number to the return-value-setting method of the
control. For example, if we expected calculate(1,
1,"/add")
to be called three times, returning 2 each
time, we could use:




control.setReturnValue(2, 3);






The general form for a method that returns
something is:




setReturnValue(
<
type
>
value, int count)






For a void method (i.e., no return value), we
can use:




control.setVoidCallable(int count)







If more than the expected number of calls
is made, an exception will be thrown on the first call that exceeds
the expectation. If fewer than the expected number of calls are
made, an exception will be thrown by the control when it is asked
to verify the expectations. The following two examples show
this.



First, an example of setting an expectation for
two calls, and making three.




public void testTooManyCalls() throws CalculatorException {
MockControl control = EasyMock.controlFor(IntCalculator.class);
IntCalculator mockCalculator = (IntCalculator)control.getMock();
mockCalculator.calculate(1, 1, "/add");
control.setReturnValue(2, 2);
control.activate();

mockCalculator.calculate(1, 1, "/add");
mockCalculator.calculate(1, 1, "/add");
mockCalculator.calculate(1, 1, "/add");

control.verify();
}






We get the following exception trace on the
third call to calculate() (trimmed for clarity and
space):




junit.framework.AssertionFailedError:
EasyMock for interface
com.saorsa.tddbook.samples.mockobjects.IntCalculator:
method call calculate(1, 1, "/add"): calls expected: 2, received: 3
at org.easymock.AbstractMockControl.invoke(AbstractMockControl.java:55)
at $Proxy0.calculate(Unknown Source)
at com.saorsa.tddbook.samples.mockobjects.TestEasyMockXmlCalculator
.testTooManyCalls(TestEasyMockXmlCalculator.java:78)






The second example is of setting the same
expectation (two calls) and only making one.




public void testTooManyCalls() throws CalculatorException {
MockControl control = EasyMock.controlFor(IntCalculator.class);
IntCalculator mockCalculator = (IntCalculator)control.getMock();
mockCalculator.calculate(1, 1, "/add");
control.setReturnValue(2, 2);
control.activate();

mockCalculator.calculate(1, 1, "/add");

control.verify();
}






In this case we don't get the expectation until
verify() is called:




junit.framework.AssertionFailedError:
EasyMock for interface
com.saorsa.tddbook.samples.mockobjects.IntCalculator:
Expectation failure on verify:
method call calculate(1, 1, "/add"): calls expected: 2, received: 1
at org.easymock.AbstractMockControl.verify(AbstractMockControl.java:45)
at com.saorsa.tddbook.samples.mockobjects.TestEasyMockXmlCalculator
.testNotEnoughCalls(TestEasyMockXmlCalculator.java:114)




















Sometimes we will want a mock method to throw an
exception when called rather than return a value. To do this, use
setThrowable() rather than setVoidCallable() or
setReturnValue().Here's an example:




control.setThrowable(new RuntimeException());






Note that you can specify the number of expected
calls as with setVoidCallable() and
setReturnValue().



One nice feature of EasyMock is that after
making the method call to set the expected arguments, you can make
repeated calls to setVoidCallable(),
setReturnValue(), and setThrowable() (with the
count parameter) to specify behavior for each subsequent call. I
don't have a good example from our Calculator code, but here's one
from the EasyMock documentation:




mockRepository.getText("Text");
control.setReturnValue("The text you wanted",2);
control.setThrowable(new RuntimeException(), 3);
control.setReturnValue("The text you really wanted");






Throwing an AssertionFailedError is the
default behavior formethods in the mocked interface for which you
haven't set an expectation, or methods called with arguments other
than those set in an expectation. You can change the default for
methods called with arguments other than those specified by using
one of the following, after the expectation-setting call to the
mock:






<div>
[View full width]</div>
control.setDefaultReturnValue(
<
type
>
toBeReturned); control.setDefaultThrowable(Throwable

toBeThrown); control.setDefaultVoidCallable()






To change the default behavior for methods for
which you set no expectation, you can use a nice control for which the default
behavior is to return the appropriate null value (i.e., 0,
null, or false). We get a nice control by using
the following factory method in EasyMock:




MockControl control = EasyMock.niceControlFor(AnInterface);


















































Amazon






No comments:

Post a Comment