EasyMock

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.

Main benefits

Main drawbacks


Installation

EasyMock has been tested on Windows NT and on Windows 98 using Sun's Java 1.3.1 and 1.4 beta.

  1. Java (at least 1.3.1) must be installed. Please note that due to a bug in Sun's Java 1.3.0, Java 1.3.1 is the minimum requirement.
  2. JUnit (at least 3.7) must be installed and in your class path.
  3. Unzip the EasyMock zip file (easymock0.8.zip). It contains a directory easymock0.8. Add the EasyMock jar file (easymock.jar) from this directory to your classpath.
For testing, add 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.


Usage

As an example, we use the interface Repository:
public interface Repository {

    void removeText(String name);
    String getText(String name);
    boolean exists(String name);

    void sync() throws IOException;
}

An empty mock object

The following examples assume that you are familiar with the JUnit testing framework and with the concept of mock objects. We will now build a test case and toy around with it to understand the functionality of the EasyMock package. To use EasyMocks, we only need one class, an instance of the 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
  1. get a MockControl for the Interface,
  2. get the mock object from the MockControl,
  3. specify the behaviour of the mock object (setup state)
  4. activate the mock object via the control (active state).
Sounds difficult? It isn't:
    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)

Adding Behaviour

Now we give our mock object some behaviour. We want it to respond to mockRepository.removeText("Text"). To get this, we have do to the following in the setup state:
    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)

Verifying Behaviour

There is one error case that we have not concerned so far: If we specify a behaviour, we have to verify that it is actually needed! The following code does not show an error:
    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)

Expecting an explicit number of calls

Up to now, our mock only checks whether the method is called at all. We can also specify an explicit call count as parameter to 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)

Specifying return values

Our interface also contains the methods 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

No surprises here! For to ensure that the mock really returns the right values, we add some assertions:
    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)

Working with Exceptions

For specifying exceptions (more exact: Throwables) to be thrown, the control has the methods 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

The only special thing here is that to specify a behaviour for a method that throws a checked exception, you have to embed the call it in a try-catch-clause.
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 ...
    }

Changing behaviour for the same method call

It is also possible to specify a changing behaviour for a method. You want getText("Text") to The implementation is straightforward:
    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 ...
    }

InvalidMockUsageException

Up to this point, at least one questions is open: What happens if we do something wrong? For example, specify a wrong return value? Or calling verify() in setup mode? Or specifying an Exception that cannot be thrown by the method?

The answer is: In all this cases, a InvalidMockUsageException will be thrown.

Reusing a mock object

An easy mock can be reset by control.reset().

Using default behaviour for methods

Up to now, the default behaviour for each method is to throw an 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 ...
    }

Nice mocks

On a mock generated by a 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:

Changes since 0.6:

Changes since 0.5: