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 Listevents = 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.