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);
|
No comments:
Post a Comment