jMock and JSF FacesContext (mocking classes)

JMock and JavaServer Faces

For the current project I am on we are using JSF for our UI. Our controller objects often make use of a FacesContext object. This makes testing controllers a bit difficult, as FacesContext is not (and does not implement) an interface. A static method on the FacesContext is used to obtain a concrete implementation of itself, further complicating matters.

So how to test a controller with code such as:

...
FacesContext facesContext = FacesContext.getCurrentInstance();
facesContext.aMethodOnAConcreteObject();
...

A solution involves a minor refactoring of the above code combined with switching on JMock’s ability to mock classes instead of interfaces.

First, enable jMock to mock classes (more details at jmock.org):

import org.jmock.Mockery;
import org.jmock.Expectations;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.jmock.lib.legacy.ClassImposteriser;

@RunWith(JMock.class)
public class ConcreteClassTest {

    private Mockery context = new JUnit4Mockery() {{
        setImposteriser(ClassImposteriser.INSTANCE);
    }};

   @Test
   private someTestMethod() {
         FacesContext facesContext = this.context.mock(FacesContext.class);
         // set up expectations as usual
         ...
   }

}

Of course, the static call to obtaining the instance is still problematic. One way to get around this is with using a getter method in the controller to wrap the call (yes, there are other ways to do this without modifying non-test code):

public class ClassUsingFacesContext {

	private FacesContext facesContext;

	public FacesContext getFacesContext() {
		if (this.facesContext == null) {
			return FacesContext.getCurrentInstance();
		}
		return facesContext;
	}

	// for testing purposes only
	public void setFacesContext(FacesContext facesContext) {
		this.facesContext = facesContext;
	}
}

and inside the test, set the controller with the mock FacesContext:

//
		final FacesContext mockFacesContext = this.context.mock(FacesContext.class);
		this.classUsingFacesContext = new ClassUsingFacesContext();
		this.classUsingFacesContext.setFacesContext(mockFacesContext);

		// set some expectations (example usage)

		final ExternalContext externalContext = this.context.mock(ExternalContext.class);
		final HttpServletRequest httpServletRequest = this.context.mock(HttpServletRequest.class);

		context.checking(new Expectations(){{
			one(facesContext).getExternalContext(); will(returnValue(externalContext));
			one(externalContext).getRequest(); will(returnValue(httpServletRequest));
			one(httpServletRequest).getServerName(); will(returnValue("serverName"));
		}});

It's only fair to share...
Share on FacebookGoogle+Tweet about this on TwitterShare on LinkedIn

Leave a Reply