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.