Testing JPA code with Mockito answers

Introduction

In JPA when we want to insert an entity in the database we use the EntityManager.persist(..) method. In addition to saving the entity the persist method will also assign a value to the field of the entity annotated with the @Id annotation (according to the configured @GenerationType). This behaviour is convenient but can be a bit tricky to test. To illustrate this lets use the following example.

Defining the problem

Lets say that we have a simple entity that represents a payment:

@Entity
public class Payment {
  @Id
  private Integer id;
  private BigDecimal amount;
  // getters and setters omitted
}

and that we want to test the method that inserts a new payment in to the database:

pubic class PaymentRepository {

  @PersistentContext
  EntityManager entityManager;

  @Inject
  EventRegistry eventRegistry;

  public void savePayment(Payment payment) {
    Preconditions.checkArgument(payment.getId() == null,
      "Must be a new payment");
    entityManager.persist(payment);
    eventRegistry.register(
      new SuccessfulPaymentEvent(payment.getId()));
  }
}

The above method checks if the payment is new, then saves the payment and then raises an appropriate event (SuccessfulPaymentEvent). For this method we would probably want to create two test cases. One when we try to create a payment that already exists and one for the case where the payment is successfully saved.

The first test case is trivial to implement:

@Test(expected=IllegalArgumentException.class)
public void failure_when_payment_exists() {
  PaymentRepository paymentRepo = new PaymentRepository();
  Payment payment = new Payment();
  payment.setId(10);

  paymentRepo.savePayment(payment);
}

In the second test case one of the things we want to verify is that the SuccessfulPaymentEvent is created with the correct payment id. To do so we have to assert that it is not null and also make sure it is the same as the id of the saved payment. This is not straightforward to implement because of the precondition check at the first line of the method. If we didn’t have the precondition then we could implement this using a Mockito ArgumentCaptor and pass a Payment with the id field already set to the savePayment(..) method. To illustrate the problem lets write a failing test and get an idea of what we are trying to achieve.

@Test
public void register_event_when_the_payment_is_successfully_saved() {
  PaymentRepository paymentRepo = new PaymentRepository();
  paymentRepo.entityManager = Mockito.mock(EntityManager.class);
  paymentRepo.eventRegistry = Mockito.mock(EventRegistry.class);
  ArgumentCaptor<SuccessfulPaymentEvent> argument =
    ArgumentCaptor.forClass(SuccessfulPaymentEvent.class);

  Payment payment = new Payment();
  paymentRepo.savePayment(payment);

  Mockito.verify(paymentRepo.entityManager).persist(payment);
  Mockito.verify(paymentRepo.eventRegistry).register(argument.capture());

  Integer paymentId = argument.getValue().getPaymentId();
  Assert.assertNotNull(paymentId); // Fails here!
  Assert.assertEquals(payment.getId(), paymentId);
}

The above test will fail because the paymentId is null in the first assertion of the test. To overcome this obstacle what we have to do is find a way to inject a known id value to the payment object when we pass it to the persist method of EntityManager. We can do this by using another Mockito feature called Answer.

A solution using Mockito

An Answer can be used to specify the action that is executed and the return value that is returned when you interact with a Mockito mock. So for our problem all we have to do is create an Answer that extracts the payment object from arguments of the mock invocation and sets its id to a known value. Then we can use this known value to assert the id of the captured SuccessfulPaymentEvent.

import org.mockito.stubbing.Answer;
import org.mockito.invocation.InvocationOnMock;

public class PaymentIdSetter extends Answer<Void> {

  private Integer paymentId;

  public PaymentIdSetter(Integer paymentId) {
    this.paymentId = paymentId;
  }

  @Override
  public Void answer(InvocationOnMock invocation) throws Throwable {
    Assert.assertEquals(1, invocation.getArguments().length);
    Payment payment = (Payment) invocation.getArguments()[0];
    payment.setId(paymentId);
    return null;
  }
}

Below we change our test case to use the PaymentIdSetter answer.

@Test
public void register_event_when_the_payment_is_successfully_saved() {
  Integer expectedId = 15;
  PaymentRepository paymentRepo = new PaymentRepository();

  paymentRepo.entityManager = Mockito.mock(EntityManager.class);
  Mockito.doAnswer(new PaymentIdSetter(expectedId))
    .when(paymentRepo.entityManager.persist(Matchers.any(Payment.class)));

  paymentRepo.eventRegistry = Mockito.mock(EventRegistry.class);
  ArgumentCaptor<SuccessfulPaymentEvent> argument =
    ArgumentCaptor.forClass(SuccessfulPaymentEvent.class);

  Payment payment = new Payment();
  paymentRepo.savePayment(payment);

  Mockito.verify(paymentRepo.entityManager).persist(payment);
  Mockito.verify(paymentRepo.eventRegistry).register(argument.capture());

  Integer paymentId = argument.getValue().getPaymentId();
  Assert.assertNotNull(paymentId);
  Assert.assertEquals(expectedId, paymentId);
}

Generalizing PaymentIdSetter

The test case required a bit more setup that is usual but it is able to verify the desired expectation. To improve this further we can easily change the PaymentIdSetter to support all the entity classes of our project either by using reflection to set the id value of the entities or by using a common parent interface (or abstract class) with a setId(..) method for our entity classes.

A possible implementation of the common parent interface option is the following:

public interface IdentifiableEntity {
  Integer getId();
  void setId(Integer id);
}
@Entity
public class Payment implements IdentifiableEntity {
  @Id
  private Integer id;
  private BigDecimal amount;

  public void setId(Integer id) {
    this.id = id;
  }

  // other getters and setters omitted
}
public class EntityIdSetter extends Answer<Void> {

  private Integer id;

  public EntityIdSetter(Integer id) {
    this.id = id;
  }

  @Override
  public Void answer(InvocationOnMock invocation) throws Throwable {
    Assert.assertEquals(1, invocation.getArguments().length);
    IdentifiableEntity entity = (IdentifiableEntity) invocation.getArguments()[0];
    entity.setId(id);
    return null;
  }
}

Conclusion

In this post we have seen how using a custom Mockito Answer enables us to effectively test code that invokes the EntityManager.persist(..) method. Of course this mechanism is not only applicable to JPA’s persist method and applies to testing code that calls methods that modify their parameters. JPA just provided us a nice example to explore this technique.

Statically weaving JPA entities for EclipseLink using Maven

EclipseLink provides advanced JPA features such as lazy-loading, change tracking and fetch groups using bytecode weaving. To use bytecode weaving you can either dynamically instrument your entity classes at runtime (via a jvm agent) or use a tool to statically process the .class files after compilation. In this post we will present how to use EclipseLink’s static weaver in a Maven project.

To enable static weaving in a Maven based project we have to add the Eclipselink weaver in the process-classes phase of the Maven’s build life cycle. The process-classes phase happens after the compile phase and allows the post-processing of files generated in compile phase. In our case, the EclipseLink weaver will post-process the .class files produced by the compiler to add extra bytecodes that implement the desired JPA functionality (lazy-loading, etc).

In the below code we use the Maven AntRun plugin to call (via the java ANT task) the command line version of the EclipseLink’s static weaver. Please note that the class name of the weaver is org.eclipse.persistence.tools.weaving.jpa.StaticWeave and not org.eclipse.persistence.tools.weaving.StaticWeave as the EclipseLink JPA Extensions wiki page says.

<build>
 <plugins>
   <plugin>
     <artifactId>maven-antrun-plugin</artifactId>
     <executions>
       <execution>
         <phase>process-classes</phase>
         <configuration>
           <tasks>
             <java classname="org.eclipse.persistence.tools.weaving.jpa.StaticWeave"
                   classpathref="maven.runtime.classpath" fork="true">
               <arg line="-loglevel FINE -persistenceinfo src/main/resources target/classes target/classes"/>
             </java>
           </tasks>
         </configuration>
         <goals>
           <goal>run</goal>
         </goals>
       </execution>
     </executions>
   </plugin>
 </plugins>
</build>

In addition to the pom.xml changes in order to use static weaving in EclipseLink you have to set the eclipselink.weaving property to static in the META-INF/peristence.xml file.

<property name="eclipselink.weaving" value="static" />

For more information about EclipseLink’s static weaving see the How to Configure Static Weaving for JPA Entities section of the Using EclipseLink JPA Extensions wiki page.

Oracle Toplink is now open source

Oracle has released the Toplink O/R mapping framework as open source. They proposed a new persistence project, named Eclipse Persistence Platform (or EclipseLink for short), at the Eclipse Foundation and they are donating the Toplink source code to start the project.

The EclipseLink will be a runtime project offering only libraries and no IDE tooling. Other Eclipse projects, like Dali, offer tooling for JPA and O/R mapping. The project will include the Toplink O/R mapping tools and APIs, a JPA implementation, an OXM framework, a SDO implementation and other persistence related technologies.

Toplink is a very mature and successful product. It is maybe the fist successful O/R mapping product for Java and it is great to see Oracle donating it to the open source community.

For more information see: