Version 0.8 (August 8 2001)
Copyright (c) 2001 OFFIS.
EasyMock is a class library that provides an easy way to use mock objects for given interfaces. EasyMock is available under the terms of the MIT license.
A mock control object gives a mock object for an interface. In the setup state these two objects (the control and the mock) are used to define valid method calls and return values or thrown exceptions as well as expectations how often a method is called.
After changing into the active state, the mock object behaves exactly as specified in the setup state.
EasyMock has been tested on Windows NT and on Windows 98 using Sun's Java 1.3.1 and 1.4 beta.
easymock0.8.zip
). It contains a directory
easymock0.8
. Add the EasyMock jar file (easymock.jar
) from this directory to your
classpath.
easymocktests.jar
to your class path and execute
'java org.easymock.tests.AllTests'
.
Since version 0.8, the full source code including the unit tests is available in the jar file src.jar
.
Repository
:
public interface Repository { void removeText(String name); String getText(String name); boolean exists(String name); void sync() throws IOException; }
MockControl
interface, that is returned
by the factory EasyMock
. As we want to write a test case, we also import
junit.framework.TestCase
:
import org.easymock.EasyMock; import org.easymock.MockControl; import junit.framework.TestCase; public class EasyMockTest extends TestCase { public EasyMockTest(String name) { super(name); } protected void setUp() { } public void testEasyMock() { } }To get a usable mock object, we need to
MockControl
for the Interface,
MockControl
,
private MockControl control; private Repository mockRepository; protected void setUp() { control = EasyMock.controlFor(Repository.class); // 1 mockRepository = (Repository) control.getMock(); // 2 } public void testEasyMock() { // 3 control.activate(); // 4 // Use the mock object! }After activating in step 4),
mockRepository
is a mock object for the Repository
interface that implements no behaviour. This means that if we try to call any
of the interface's methods, the mock object will throw an AssertionFailedError
:
public void testEasyMock() { // 3 control.activate(); // 4 // Use the mock object! mockRepository.removeText("Text"); // throws AssertionFailedError }throws
junit.framework.AssertionFailedError: EasyMock for interface Repository: Unexpected method call removeText("Text") at org.easymock.AbstractMockControl.invoke(AbstractMockControl.java:55) at $Proxy5.removeText(Unknown Source) at EasyMockTest.testEasyMock(EasyMockTest.java:26)
mockRepository.removeText("Text")
.
To get this, we have do to the following mockRepository.removeText("Text")
public void testEasyMock() { mockRepository.removeText("Text"); // 3 control.setVoidCallable(); // 3 control.activate(); // 4 mockRepository.removeText("Text"); // No exception throwing now }Since Release 0.7, there is also a shortcut: If a void method is called in the setup state, and no behaviour is specified,
setVoidCallable()
is done automatically. So the line control.setVoidCallable(); // 3
can be removed:
public void testEasyMock() { mockRepository.removeText("Text"); control.activate(); mockRepository.removeText("Text"); // No exception throwing now }In the setup state, the mock does not behave like a mock, but it is used to make the method calls for which we define the behaviour on the control! If you want to make this more explicit, you can give it an other name in this phase:
public void testEasyMock() { MockControl mockControl = EasyMock.controlFor(Repository.class); Repository methodCallControl = (Repository) mockControl.getMock(); methodCallControl.removeText("Text"); mockControl.setVoidCallable(); mockControl.activate(); Repository mockRepository = (Repository) mockControl.getMock(); mockRepository.removeText("Text"); }However, we will stick to using only the mockRepository variable.
If we call mockRepository.removeText("Text")
again, nothing happens.
If we call mockRepository.removeText()
with another parameter (for example "Text2"),
we get an AssertionFailedError
:
junit.framework.AssertionFailedError: EasyMock for interface Repository: Unexpected method call removeText("Text2") at org.easymock.AbstractMockControl.invoke(AbstractMockControl.java:55) at $Proxy9.removeText(Unknown Source) at EasyMockTest.testEasyMock(EasyMockTest.java:27)
public void testEasyMock() { mockRepository.removeText("Text"); control.setVoidCallable(); control.activate(); // no usage of specified behaviour! }To verify that all the specified behaviour has been used, we have to call
control.verify()
:
public void testEasyMock() { mockRepository.removeText("Text"); control.setVoidCallable(); control.activate(); // no usage of specified behaviour! control.verify(); // throws AssertionFailedError }Then we get the following exception:
junit.framework.AssertionFailedError: EasyMock for interface Repository: Expectation failure on verify: method call removeText("Text"): calls expected: at least 1, received: 0 at org.easymock.AbstractMockControl.verify(AbstractMockControl.java:45) at EasyMockTest.testEasyMock(EasyMockTest.java:28)
setVoidCallable()
. For testing,
we call the method too many times and see what happens.
public void testEasyMock() { mockRepository.removeText("Text"); control.setVoidCallable(3); control.activate(); mockRepository.removeText("Text"); mockRepository.removeText("Text"); mockRepository.removeText("Text"); mockRepository.removeText("Text"); // throws AssertionFailedError mockRepository.removeText("Text"); control.verify(); }We get the following exception that tells us that the method has been called too many times. The failure occurs directly at the first method call exceeding the limit.
junit.framework.AssertionFailedError: EasyMock for interface Repository: method call removeText("Text"): calls expected: 3, received: 4 at org.easymock.AbstractMockControl.invoke(AbstractMockControl.java:55) at $Proxy16.removeText(Unknown Source) at EasyMockTest.testEasyMock(EasyMockTest.java:30)If we delete the last three
mockRepository.removeText("Text")
-calls, the
control.verify()
throws an AssertionFailedError
:
junit.framework.AssertionFailedError: EasyMock for interface Repository: Expectation failure on verify: method call removeText("Text"): calls expected: 3, received: 2 at org.easymock.AbstractMockControl.verify(AbstractMockControl.java:45) at EasyMockTest.testEasyMock(EasyMockTest.java:30)
String getText(String name)
and
boolean exists(String name)
. For specifying return values, the MockControl
has the methods setReturnValue([type] value)
and
setReturnValue([type] value, int times)
. [type]
is either Object
or a primitive type.
As an example, we specify that our mock object should respond to
getText("Text")
with "The text you wanted"
an unlimited number of times
getText("Text2")
with null
exactly three times and to
exists("Text")
with true
exactly two times.
public void testEasyMock() { mockRepository.getText("Text"); control.setReturnValue("The text you wanted"); mockRepository.getText("Text2"); control.setReturnValue(null, 3); mockRepository.exists("Text"); control.setReturnValue(true, 2); control.activate(); assertEquals("The text you wanted", mockRepository.getText("Text")); assertNull(mockRepository.getText("Text2")); assertTrue(mockRepository.exists("Text")); control.verify(); }In this case, we can see that verify() collects all the verification errors:
junit.framework.AssertionFailedError: EasyMock for interface Repository: Expectation failure on verify: method call getText("Text2"): calls expected: 3, received: 1 method call exists("Text"): calls expected: 2, received: 1 at org.easymock.AbstractMockControl.verify(AbstractMockControl.java:45) at EasyMockTest.testEasyMock(EasyMockTest.java:35)
setThrowable(Throwable throwable)
and
setThrowable(Throwable throwable, int times)
.
Unchecked exceptions (that is, RuntimeException
, Error
and all their subclasses) can be thrown from every method. Checked exceptions can only be
thrown from the methods that do actually throw them.
As an example, we specify that our mock object should respond to
getText("Text")
with a RuntimeException
exactly two times
void sync()
with an IOException
.
import java.io.IOException; // ... public void testEasyMock() { mockRepository.getText("Text"); control.setThrowable(new RuntimeException(), 2); try { mockRepository.sync(); control.setThrowable(new IOException()); } catch (IOException shouldNeverHappen) { throw new Error("bug in EasyMock library"); } control.activate(); // use the mock ... }
getText("Text")
to
"The text you wanted"
the first two times,
RuntimeException
the next three times,
"The text you really wanted"
afterwards?
public void testEasyMock() { mockRepository.getText("Text"); control.setReturnValue("The text you wanted", 2); control.setThrowable(new RuntimeException(), 3); control.setReturnValue("The text you really wanted"); control.activate(); // use the mock ... }
The answer is: In all this cases, a InvalidMockUsageException
will be thrown.
control.reset()
.
AssertionFailedError
.
This can be changed by calling setDefaultReturnValue()
,
setDefaultThrowable()
or setDefaultVoidCallable()
in the setup state.
The following code configures the mock to answer "The text you wanted" on getText("Text")
and "The default text" for all other parameters to getText()
:
public void testEasyMock() { mockRepository.getText("Text"); control.setReturnValue("The text you wanted"); control.setDefaultReturnValue("The default text"); control.activate(); // use the mock ... }
MockControl
returned by EasyMock.controlFor()
,
the default behaviour for each method is to throw an AssertionFailedError
.
If we want a "nice mock" that by default allows all method calls and returns appropriate empty values (0, null or false),
we simply use EasyMock.niceControlFor()
.
EasyMock is developed and maintained by Tammo Freese.
Thanks to the beta testers and first users for their helpful comments, suggestions
and bug reports:
George Dinwiddie, Dierk Koenig, Robert Leftwich, Karsten Menne, Frank Westphal, and Bernd Worsch.
Please check the EasyMock home page for new versions.
Please send bug reports and suggestions to Tammo Freese.
EasyMock Version 0.8 (August 8 2001)
Changes since 0.73:
Changes since 0.7:
equals()
, hashCode()
and toString()
now also work with Java 1.3
de.offis.easymock.tests
Changes since 0.6:
equals()
, hashCode()
and toString()
now cannot be redefined
setVoidCallable()
is automatically assumed if missing
MockControl
for mock controls
Changes since 0.5:
verify()
in setup state throws an InvalidMockUsageException
now
addReturnValue(...)
- methods renamed to setReturnValue(...)
setOpenVoidCallCount()
renamed to setVoidCallable()
setExpectedVoidCallCount(int times)
renamed to setVoidCallable(int times)
addThrowable(...)
renamed to setThrowable(...)
AssertionFailedError
s provide information