Mock objects are objects that pretend to be instances of particular classes, but are completely controlled by the test developer. They appear to implement the specified interfaces, and you can configure them to return predictable values when their meth- ods are called. You can use these mock objects instead of references to other Spring beans or collaborating classes.
There are a number of different mocking frameworks available for unit testing, including these:
Mockito (http://mockito.org)—A popular unit test mocking library, well known for its easy-to-understand syntax and literate API, which uses method names such as when, then, answer, and other English language predicates. The Mockito API is great for mocking Spring bean and regular POJO collaborators with methods defined by an interface.
EasyMock (http://easymock.org)—Mocks primarily based on interfaces. Uses a mock expectation recording and playback API, which can be a bit daunting to new users. In addition to Spring’s interface-driven applications, it’s a popular library.
JMock (http://jmock.org)—Mocks based on interfaces as well. Developers define expected behavior and then execute their code.
PowerMock (http://code.google.com/p/powermock)—An extension library that enhances the capabilities provided by EasyMock and Mockito. This mock- ing tool can mock static methods, interfaceless classes, constructors, final classes, and so on, which other APIs cannot.
Mock objects are often used when a layered application requires a particular Spring bean to collaborate with other beans, either in the same level or lower levels of the application. They take less time to create than a fully stubbed object, because you only have to create the mock object at runtime, define the specific behavior to mock, and ignore the rest of the methods in the class. Stubs require you to create a concrete class, and implement each of the methods of the object under test. That takes a lot of time, and unless you’re able to exercise the entire stub across all of your test methods, it may be a waste of your time.
What if you want to see whether the entity can be validated appropriately, or if you need to stub the entity itself behind a service? Spring provides a feature to mock the
1 Refer to JUnit in Action, Second Edition for details on how to work with these APIs.
persistence layer enough to test your validations, without requiring a full JPA stack.
This lets you perform basic unit tests against your entities, exercising logic you may have stashed away in assertTrue methods or methods that may contain business logic, and perform operations to generate derived data.
9.3.1 Mocking services with Mockito
Here’s how you can use mocks to emulate collaborating objects:
Create your mock objects and define expectations.
Create an instance of your object under test, manually setting or injecting your mock collaborators.
Exercise the method under test.
Verify your test results.
If you were working within a traditional, layered Spring application, you’d likely have Spring service beans that would work with Data Access Objects. You could easily make a mock version of a DAO that returns a predefined list of students if the getAllStudents() method is called. A fragment of a test may look something like this example:
...
StudentDAO studentDAO = Mockito.mock(StudentDAO.class);
studentService.setStudentDAO(studentDAO);
Mockito.when(studentDAO.getAllStudents())
.thenReturn(preDefinedListOfStudents);
List<Student> students = studentService.getAllStudents();
Assert.assertTrue(students.size() == predefinedListOfStudents.size());
In the previous fragment, you’re testing a StudentService bean in isolation. It collab
orates with a Student DAO, which you need to mock to provide that isolation. You’ll use the Mockito.mock(StudentDAO.class) statement to provide the mock object, and then set the mock as your collaborating Student DAO. You then use the Mockito.when method to tell the mock object to respond with a predefined list of students (not shown) when the DAO’s getAllStudents() method is called from the service.
As you can see, the Mockito framework makes it easy to define a mock object, such as studentDAO shown in the previous example. You can then manually inject the mock into studentService and tell the mock to return a prefabricated student list. After the service requests the getAllStudents() method on your mock, it automatically returns your predefined list of students.
LEARNING MORE For more information about the excellent Mockito mocking framework, visit the project website at http://mockito.org. You might also be interested in an API that layers on top of Mockito and its cousin EasyMock.
Named PowerMock, this API allows you to mock static methods and imple
ments a number of other helpful features. See the website at http://
code.google.com/p/powermock.
221 Unit tests using mock objects
Mockito does a great job with typical, interface-driven Spring beans. But it can’t mock Roo entities, because Roo uses static methods within the entity to find entity instances from the database, and holds onto a private JPA entity manager to provide persistence logic.
All is not lost, though. Rod Johnson, the creator of Spring, contributed a testing framework element to help out: the static entity method mocking API.
9.3.2 The entity mocking framework
Spring provides a special mocking API to allow developers to write tests against static entity methods. To use it, you follow these steps:
1 Annotate your test class with @MockStaticEntityMethods.
2 Within a test method that requires collaboration with an entity, issue the method call that you’d like to mock. Because the annotation is in effect, the static method isn’t executed, but is added to the list of methods to expect.
3 Use the AnnotationDrivenStaticEntityMockingControl (for brevity, let’s abbreviate this as the acronym ADSEMC): call its .expectReturn method to define the result of the operation. You don’t need to do this if the method has no return value.
4 Put the mock in playback mode with the ADSEMC.playback() method.
5 Exercise the mock.
Because of the static methods used by the entity finders, testing a Spring bean involv- ing an entity requires use of the Spring entity mocking framework.
9.3.3 Creating an entity mock test
To set up a mock test case, you use the test mock command:
roo> test mock --entity ~.model.Course
Roo will then create a test class that looks like the example shown in the following listing.
package org.foo.model;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.springframework.mock.staticmock.MockStaticEntityMethods;
@RunWith(JUnit4.class)
@MockStaticEntityMethods public class CourseTest {
@Test
public void testMethod() { int expectedCount = 13;
Course.countCourses();
Listing 9.1 A mock course test
Enable mocking
Record expectation
org.springframework.mock.staticmock.➥ Expected
AnnotationDrivenStaticEntityMockingControl ➥ result
.expectReturn(expectedCount);
org.springframework.mock.staticmock.➥ Watch
AnnotationDrivenStaticEntityMockingControl.playback();
for result org.junit.Assert.assertEquals(➥
expectedCount, Course.countCourses());
} }
In the generated sample, the test is expected to call the Course.countCourses() method, returning the result of 13 rows. The @MockStaticEntityMethods annotation places the entity mock in expectation record mode. At this point, any calls to static methods, such as the Course.countCourses() method, won’t execute, but will be added to the recording queue.
Next, you use the AnnotationDrivenStaticEntityMockingControl object to record an expectation: a definition of the behavior to trigger when calling the last method recorded. In this case, you want the countCourses() method invocation to return the value 13 when executed.
Finally, you put the mock in playback mode, and then call your Course.count- Courses() method, which now returns the value 13, which you can check against your assertion.
THIS SEEMS COMPLEX While it’s true that the setup for this test is a bit unwieldy, you benefit because you’re able to test your validation assertions and plain business logic within a unit test, rather than booting the Spring container. Unit tests run in a fraction of the time.
You’ll need to decide whether to use integration tests for accuracy, or unit tests for speed, when testing validations and business logic attached to your entities.
You’ve examined the test mock command, and how to make use of it to write unit tests of Spring Roo entities. Now it’s time to use this in a more complex example and test what happens when you try to validate data.
9.3.4 Unit testing the completeRegistration() method
A typical JUnit unit test will test a single “unit” of code, such as a method on a Spring bean. Everything else is either stubbed or mocked, so that you can focus your energies on exercising your method under test and verifying the result.
Now let’s test an example Spring service method, RegistrationService .completeRegistration(long offeringId, List<Long> studentIds). The imple
mentation looks like this:
public class RegistrationServiceDefaultImplBean implements RegistrationService { @Override
223 Unit tests using mock objects
@Transactional
public void completeRegistration(
Long offeringId, List<Long> studentIds) {
Offering offering = Offering.findOffering(offeringId);
for (Long studentId : studentIds) {
Student student = Student.findStudent(studentId);
Registration registration = new Registration();
registration.setStudent(student);
registration.setOffering(offering);
registration.setAttended(false);
registration.setPaymentMade(false);
offering.addRegistration(registration);
registration.persist();
} } }
The method creates a number of Registration entities and associates them with the appropriate student and course offering. Figure 9.3 shows the order of operations.
The previous sequence diagram shows a fairly typical interaction with Roo’s Active Record–based entities. Rather than relying on repositories or DAOs to wrap access to your entities, you use the static finder methods to locate them, and methods such as persist() and merge() to manipulate them. You also define transactionality on this
Figure 9.3 The completeRegistration() method
method, so that all operations against the entities are either committed or rolled back automatically.
Testing this method in the Spring container, as you’ll see in section 9.4, is more involved and requires booting the application server. If you’re purely testing applica
tion logic, and can emulate the data access routines rather than take the integration testing overhead, you can run the test as a unit test, and improve the speed of your build process as a result.
Now, let’s implement the test using JUnit and Roo’s own entity mocking framework.
9.3.5 Mocking with the RegistrationServiceBean
Let’s write a test that exercises a bean that processes student registrations. The bean provides a registerStudents method that accepts a set of students and an offering, and then registers students for the provided course offering.
SETTING UP THE TEST
First, you’ll create the unit test class, using the Roo Shell test mock command:
roo> test mock --entity ~.service.RegistrationServiceBeanImpl
Note that the term --entity can be any class definition. Let’s open the bean defini
tion for the newly created RegistrationServiceBeanImplTest and verify the class definition. Roo should define your test class with the @MockStaticEntityMethods annotation:
@MockStaticEntityMethods
public class RegistrationServiceDefaultImplBeanTest {
This annotation enables the entity mocking framework, as you’ll see in the next example.
Now, you’ll define three DataOnDemand class instances, one each for Student, Offering, and Course, which you’ll use to decrease the amount of code in your test.
Define them in the Roo console with the dod command if they’re missing in your code:
private StudentDataOnDemand studentDod;
private OfferingDataOnDemand offeringDod;
private CourseDataOnDemand courseDod;
You’ll also create the Service bean yourself, so hold onto a reference to that, too:
private RegistrationServiceDefaultImplBean
registrationServiceDefaultImplBean;
SETTING UP THE BEAN AND DATAONDEMAND CLASSES
Next, define a JUnit @Before method, setup(), which executes before each test. This method will create instances of all four of these objects:
@Before
public void setUp() {
registrationServiceDefaultImplBean =
new RegistrationServiceDefaultImplBean();
225 Unit tests using mock objects
studentDod = new StudentDataOnDemand();
offeringDod = new OfferingDataOnDemand();
courseDod = new CourseDataOnDemand();
}
Now, for each method under test, JUnit will create a new instance of the Registration- Service Bean, then reinitialize all three DataOnDemand instances.
RECORD TEST EXPECTATIONS
Define your test method, testRegisterStudents(). You’ll annotate it with @Test:
@Test
public void testRegisterStudents() { ...
}
Now, let’s write the code for the test. Referring back to the sequence diagram in figure 9.3, you’ll see that the first action of the method under test is to look up an Offering based on the value of the offering key passed to it.
You’ll use the OfferingDataOnDemand class to generate a test offering, and assign a fake primary key value of 1:
Offering offering = offeringDod.getNewTransientOffering(1);
offering.setId(1L);
Now, you’ll invoke the call you’d like to have your unit execute when it’s under test, and you’ll follow that up with what you expect it to return:
Offering.findOffering(1L);
AnnotationDrivenStaticEntityMockingControl.expectReturn(offering);
You’ll do this for every interaction you expect your unit to invoke.
For example, you can pass in ten Long primary key fields of students to register for this class. To set up the condition, do the following:
List<Long> ids = new ArrayList<Long>();
List<Student> testStudents = new ArrayList<Student>();
// record expectations
for (int id = 0; id < 10; id++) { Student.findStudent((long)id);
Student student = studentDod.getNewTransientStudent(id);
student.setId(Long.valueOf(id));
ids.add(Long.valueOf(id));
AnnotationDrivenStaticEntityMockingControl.expectReturn(student);
}
By now we hope you’re thinking that a straightforward integration test would be eas- ier than this. You’re right, but it’ll run slowly and you’ll have to run it against a live database. If you’re trying to make sure the actions in your sequence diagram are called in the correct order, this test will do so at a fraction of the time.
TEST AND VERIFY
But you’re not done yet. You have to run the test—like EasyMock, Spring’s control doesn’t do anything until you call the playback() method:
AnnotationDrivenStaticEntityMockingControl.playback();
Now you can perform the call, using the generated offering’s primary key (1L) and the list of fake primary keys generated for each student mock call:
registrationServiceDefaultImplBean.
completeRegistration(offering.getId(), ids);
If all is well, there’ll now be ten registrations, which you’ll fetch and assert, as shown in this next example:
Set<Registration> registrations = offering.getRegistrations();
Assert.assertEquals(10, registrations.size());
And that’s that. About 30 lines of code, with appropriate white spacing. The main ben
efit of testing in this way is that it forces you to review the coupling of your entities and services. If you find you’re mocking too many classes, you may have approached your design without thinking about the single responsibility principle (SRP).