Learn to use EasyMock to create test mocks, record and replay the expectations and verify method invocations on mocked instances. We will be setting up EasyMock with JUnit 4 and JUnit 5, both.
1. EasyMock Dependencies
Include the latest version of easymock from the Maven repository into the project.
2. Test Steps with EasyMock
EasyMock framework creates the mock objects using the java.lang.reflect.Proxy
object. When we create a mock object, during test execution, the proxy object takes the place of the real object. The proxy object gets its fields and methods from the interface or class we pass when creating the mock.
A typical test with EasyMock has four stages: create mock, expect, replay and verify.
- Create Mock: Use
EasyMock.mock()
to create mocks of target classes whose behavior we want to delegate to the proxy objects. Generally, we mock the classes that interact with external systems or classes that should not be part of the test code. - Record Expectations: Use
EasyMock.expect()
to record the expectations from the mock objects. These expectations include simulating a method with certain arguments, the return value of the invoked method and the number of times the method should be invoked. - Replay: The
EasyMock.replay()
method makes the Mock object available. In ‘replay’ mode, when the test invokes a recorded method then the mock will return the recorded results in the previous step. - Verify: The
EasyMock.verify()
verifies that, during the test execution, all expectations were executed as recorded and that no unexpected call was performed on a mock.
We will see how to perform all these steps in section 4.
3. Setting Up EasyMock with JUnit
Before moving further, it is important to learn that we need to follow different approaches to run the tests on the basis underlying JUnit version is 4 or 5. So you can select one of the following solutions as per your project requirements.
The following solutions are used to process @Mock and @TestSubject annotations in the test class. If we are not using these annotations, then we can skip using the following solutions.
3.1. With JUnit 4
The legacy JUnit 4 uses the EasyMockRunner class to run the tests. Note that this runner only works with JUnit 4.5 or higher.
In JUnit 4, we can also use the EasyMockRule instead of EasyMockRunner, with the same effect.
3.2. With JUnit 5
In JUnit 5, Rules can’t be used anymore. The new JUnit 5 uses the EasyMockExtension class to run the tests. Since EasyMock 4.1, EasyMock ships with this JUnit 5 extension out of the box.
4. EasyMock Demo
Let’s understand all the steps in easymock with an example. We will first a few classes and the dependencies to mock, then we will write a test for it.
4.1. System Under Test
We have a RecordService
class that can be used to save Record data in a backend database. The RecordService is dependent on RecordDao
to interact with database and SequenceGenerator
to get the next valid sequence number used as Record id.
4.2. A Simple Test
In the given test, we are testing the RecordService.saveRecord()
method. The service depends on RecordDao and SequenceGenerator. The Dao interacts with database and sequence generator also interacts with database to fetch the next record id. We need to mock both dependencies as they are out of scope for this testcase.
The next step is to record expectations in both mocks. In the following lines, we are setting expectations of method invocations in both mocks, what value to return if method is invoked and how many times the method is expected to be invoked.
We can flexible matchers such as anyObject(), isA(), notNull() etc to write expectations that match a number of arguments. But we must return a concrete value from the result matchers such as andReturn() or andThrow() methods.
The invocation count is mentioned using once(), times(exactCount), times(min, max), atLeastOnce() and anyTimes().
To put the test execution in replay mode, we can use replay the mocks either one by one or combine all mocks in a single replay call.
If we do not want to keep track of all mocks in the test, we can use EasyMockSupport to replay all mocks at once.
In the replay mode, we perform the operation in the system under test. This shall invoke the recorded methods in expectations and return values from mock objects.
Finally, we verify the mocks that all expectations were met and no unexpected call happened on the mock objects. The syntax of verify() is similar to replay() method. Use one of the following options to trigger verification of mocks.
A complete example of the testcase, involving all the above steps, is as follows:
4.3. A Test using Using Annotations
The previous example directly the mock()
method to create mocks and then inject the mocks into the RecordService class. We can use @Mock and @TestSubject annotations to do this declaratively.
Note that all other steps i.e. recording expectations, replaying and verifying do not change. Only mocking is affected by this change.
4.4. A Test using Using EasyMockSupport
Apart from creating the instance of EasyMockSupport
, we can extend the test class from EasyMockSupport. In this way, we can directly access the replayAll() and verifyAll() methods.
5. Advance Concepts
5.1. Mock vs Strict Mock vs Nice Mock
EasyMock
supports three types of mock objects. Use the following methods to create mocks:
EasyMock.mock()
EasyMock.strictMock()
EasyMock.niceMock()
We can also use EasyMock.createMock()
method to create these mocks:
The behavior of these mocks is different when verifying the recorded expectations.
- Default Mock: A test fails if a method is called that is not expected or if a method that is expected is not called. Order of method calls does not matter.
- Nice Mock: A test fails if a method is expected but not called. Methods that are called but are not expected are returned with a type-appropriate default value (0, null or false). Order of method calls does not matter.
- Strict Mock: Similar to default mock except the order of method calls does matter.
Note that for mocks created by mock()
and strictMock()
, any unexpected method call would cause an AssertionError
.
The niceMock()
allows any unexpected method calls on the mock without failing the test when the method returns a type-appropriate default value.
5.2. Mocking Exceptions
In order to be able to test that a method throws the appropriate exceptions when required, a mock object must be able to throw an exception when called.
Use andThrow() method to record the expectation of an exception class.
6. Conclusion
In this EasyMock tutorial, we learned to configure easymock with Junit and execute the tests under junit 4 and junit 5 platforms. We learned the basic concepts of testing with easymock, including test steps such as mock, expect, replay and verify.
Finally, we learned to write a complete test with an example.
Happy Learning !!
Comments