Actually I am not a big fun of in-container unit testing approaches. They have longer execution times, e.g. container and database startup, intialization 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 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 delegated FacesContext object, in that case JSFUnitFacesContext instance, via reflection. As JSFUnit support 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 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, 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 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 with 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. Same jar renaming approach can be followed in order for classes with same signature in JUnit4 jar to be discovered at first place.