Integrating JSFUnit To Your Project

By Kenan Sevindik

Actually I am not a big fan of in-container unit testing approaches. They have longer execution times, e.g. container and database startup, initialization times, etc. In addition, they create configuration complexity in terms of packaging of tests and deployment of application together with them. Anyway, recently I needed to examine JSFUnit solution more closely for some issue and integrated it into my project.

JSFUnit is based on Cactus in-container unit testing framework. Tests are run on server side and results can be examined through your browser. I had presented about Cactus several years ago. I found that presentation in one of the dusty corners of my laptop. It was a nice nostalgia for me.

Anyway, JSFUnit has a really good startup document on its site. I have totally followed their step-by-step guides and it almost worked. I had a stupid error while running my tests. The problem was because of Spring WebFlow’s JSF 1.1 compatibility efforts. As you may know, SWF2 has some JSF specific parts. Their FlowFacesContext implementation tries to understand if getELContext() method is available in the delegated FacesContext object, in that case JSFUnitFacesContext instance, via reflection. As JSFUnit supports JSF 2.0, trying to understand if that method is supported via class retrospection will cause ClassNotFoundException. Here is the stacktrace:

Caused by: java.lang.NoClassDefFoundError: javax/faces/context/ExceptionHandler 
at java.lang.Class.getDeclaredMethods0(Native Method) 
at java.lang.Class.privateGetDeclaredMethods(Class.java:2427) 
at java.lang.Class.getMethod0(Class.java:2670) 
at java.lang.Class.getMethod(Class.java:1603) 
at org.springframework.util.ClassUtils.getMethodIfAvailable(ClassUtils.java:549) 
at org.springframework.faces.webflow.FlowFacesContext.getELContext(FlowFacesContext.java:97) 
at org.speedyframework.web.view.jsf.util.JsfUtils.createValueExpression(JsfUtils.java:45) 
at org.speedyframework.web.view.jsf.component.ui.Label.(Label.java:32) 
at org.speedyframework.admin.pages.common.Login.afterPropertiesSet(Login.java:40) 
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1369) 
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1335) 
... 
85 more 
Caused by: java.lang.ClassNotFoundException: javax.faces.context.ExceptionHandler 
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1360) 
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1206) 
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319) 
... 96 more

Unfortunately, there is no clean solution for such a problem in the current Java classloading model. We still wait for developments to get matured enough in OSGI area for JEE. For the moment, we just need to add JSF 2.0 API jar to the classpath, even though we still use MyFaces implementation of JSF 1.2. After adding jsf-api-2.0.jar to the classpath, the problem is carried to somewhere else. This time, JSF 2.0 classes were loaded before JSF 1.2 classes because of Java class discovery mechanics. Classloaders process jars alphabetically, and classes with the same signatures in a different jar will get loaded before your actual classes. We just have to rename jsf-api.jar to come after myfaces-api.jar by putting ‘z-‘ in front of its name to solve this problem as well.

Another annoying part during JSFUnit integration was related to the oldness of Cactus Framework. It still depends on ages-old JUnit 3.8.1, and if you use JUnit 4, it won’t work. You need to add JUnit 3.8.1 jars in your classpath. The same jar renaming approach can be followed in order for classes with the same signature in JUnit4 jar to be discovered at first place.

Share: X (Twitter) LinkedIn