Unit Testing JPA (EJB3) Code With With an EntityListener.
My goal was to verify that some objects were pulled from the EJB3 cache (ie, that I was actually getting cache hits) using unit tests. I did not see any way to get statistical information or cache information using the EJB3 API. However, I did read the about EntityListeners annotation.
@Entity
@EntityListeners( { DomainObjectListener.class })
public class DomainObject {
The EntityListeners annotation allows you to specify a class which can track EJB3 events such as PrePersist and PostPersist. I developed some simple ObjectEvent classes to track, via a list, which EJB3 events happen. The following code shows my base class and one subclass. There is one subclass for each EJB3 event.
package org.domain;
abstract public class ObjectEvent {
private IDomainObject domainObject = null;
public ObjectEvent(final IDomainObject _domainObject) {
super();
this.domainObject = _domainObject;
}
public IDomainObject getDomainObject() {
return this.domainObject;
}
}
package org.domain;
public class PrePersistObjectEvent extends ObjectEvent {
public PrePersistObjectEvent(final IDomainObject _domainObject) {
super(_domainObject);
}
}
I'm sure that you can create the rest of the ObjectEvent subclasses. The DomainObjectListener class holds a list of events and watched classes. The list of watched classes is needed because all of my persisted objects are subclasses of DomainObject. Here is the listener class:
package org.domain;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
public class DomainObjectListener {
// private static final Logger logger = Logger.getLogger(DomainObjectListener.class.getSimpleName());
final static private List watchedClasses = new ArrayList();
@SuppressWarnings("unchecked")
public static void addWatchedClass(Class clazz) {
watchedClasses.add(clazz);
}
public static void clear() {
watchedClasses.clear();
events.clear();
}
final static private List events = new ArrayList();
private static void addEvent(final ObjectEvent objectEvent) {
events.add(objectEvent);
}
public static List getEvents() {
return events;
}
public static int getNumberOfEvents() {
return events.size();
}
public static boolean wasEventGenerated(final Class objectEventClazz) {
boolean rv = false;
for (ObjectEvent objectEvent : events) {
if (objectEvent.getClass() == objectEventClazz) {
rv = true;
break;
}
}
return rv;
}
public DomainObjectListener() {
super();
}
@PrePersist
public void prePersist(final IDomainObject item) {
if (watchedClasses.contains(item.getClass())) {
addEvent(new PrePersistObjectEvent(item));
}
}
@PreRemove
public void preRemove(final IDomainObject item) {
if (watchedClasses.contains(item.getClass())) {
addEvent(new PreRemoveObjectEvent(item));
}
}
@PostPersist
public void postPersist(final IDomainObject item) {
if (watchedClasses.contains(item.getClass())) {
addEvent(new PostPersistObjectEvent(item));
}
}
@PostRemove
public void postRemove(final IDomainObject item) {
if (watchedClasses.contains(item.getClass())) {
addEvent(new PostRemoveObjectEvent(item));
}
}
@PreUpdate
public void preUpdate(final IDomainObject item) {
if (watchedClasses.contains(item.getClass())) {
addEvent(new PreUpdateObjectEvent(item));
}
}
@PostUpdate
public void postUpdate(final IDomainObject item) {
if (watchedClasses.contains(item.getClass())) {
addEvent(new PostUpdateObjectEvent(item));
}
}
@PostLoad
public void postLoad(final IDomainObject item) {
if (watchedClasses.contains(item.getClass())) {
addEvent(new PostLoadObjectEvent(item));
}
}
}
The unit tests to use the entity listener might look like this:
public void test_new_object_pre_and_post_events_only() {
DomainObjectListener.addWatchedClass(ContactCountry.class);
addContactCountry("US", "United States of America");
assertEquals(2, DomainObjectListener.getNumberOfEvents());
assertTrue(DomainObjectListener.wasEventGenerated(PrePersistObjectEvent.class));
assertTrue(DomainObjectListener.wasEventGenerated(PostPersistObjectEvent.class));
DomainObjectListener.clear();
}
I'm not happy with my solution because the entity listener class is assigned via an annotation which means that it is present in all enviroments - dev, test, and production - unless the DomainObject class is changed and recompiled during deployment. I'll keep looking for a better solution.