Sunday, March 30, 2014

Set up JSF environment for JUnit tests

JUnit tests often need mocked JSF / Servlet objects when you test JSF based web applications. Such objects can be FacesContext, ExternalContext, ApplicationContext, HttpRequest, HttpSession, etc. I already mentioned the MyFaces Test Framework in one of my outdated post. In this post, I would like to introduce a new simple and lightweight approach based on JUnit TestRule. The concept behind TestRule is similar to custom JUnit runners, but without restrictions (you can not use multiple runners, but you can use multiple TestRules). Let's go step by step to explain the idea. A class which implements the interface TestRule must implement the method
Statement apply(Statement base, Description description)
The first Statement parameter is a specific object which reprensents the method under the test from your test class. Such a test method can be invoked by base.evaluate(). You can place any custom code before and after the call base.evaluate(). A typically implementation follows this pattern
public Statement apply(final Statement base, Description description) {
    return new Statement() {
        @Override
        public void evaluate() throws Throwable {
            // do something before invoking the method to be tested
            ...
            try {
                base.evaluate();
            } finally {
                // do something after invoking the method to be tested
                ...
            }
        }
    };
}
In short words: the apply method allows to intercept the base call of every test method and put a custom code around. Your TestRule implementation, say MyRule, can be used in any test class with the @Rule annotation as follows:
@Rule
public TestRule myRule = new MyRule();
Note: The member variable should be public. Let's take more examples. There is a good introduction in this tutorial. The author demonstrates how to implement two TestRules: one for SpringContext to use @Autowired in test classes and one for Mockito to populate the mocks before each test. An excellent example! I allow me to repeat the usage example.
public class FooTest {

    @Rule
    public TestRule contextRule = new SpringContextRule(new String[]{"testContext.xml"}, this);

    @Rule
    public TestRule mockRule = new MockRule(this);

    @Autowired
    public String bar;

    @Mock
    public List baz;

    @Test
    public void testBar() throws Exception {
        assertEquals("bar", bar);
    }

    @Test
    public void testBaz() throws Exception {
        when(baz.size()).thenReturn(2);
        assertEquals(2, baz.size());
    }
}
This can not be achieved with two JUnit runners at once. E.g. you can not annotate a test class at the same time with @RunWith(Parameterized.class) and @RunWith(SpringJUnit4ClassRunner.class) or @RunWith(MockitoJUnitRunner.class).

But back to JSF. I want to show how to implement a TestRule for a simple and extensible JSF environment. First of all, we need a mock for FacesContext. We will implement it with Mockito - the most popular Java test framework. I have seen many different implementations, but in fact it is not difficult to implement a proper mock of FacesContext.
import javax.faces.context.FacesContext;

import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public abstract class FacesContextMocker extends FacesContext {

    private FacesContextMocker() {
    }

    private static final Release RELEASE = new Release();

    private static class Release implements Answer<Void> {
        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            setCurrentInstance(null);
            return null;
        }
    }

    public static FacesContext mockFacesContext() {
        FacesContext context = Mockito.mock(FacesContext.class);
        setCurrentInstance(context);
        Mockito.doAnswer(RELEASE).when(context).release();
        return context;
    }
}
For all PrimeFaces fan we will provide a similar mock for RequestContext.
import org.primefaces.context.RequestContext;

import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public abstract class RequestContextMocker extends RequestContext {

    private RequestContextMocker() {
    }

    private static final Release RELEASE = new Release();

    private static class Release implements Answer<Void> {
        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            setCurrentInstance(null);
            return null;
        }
    }

    public static RequestContext mockRequestContext() {
        RequestContext context = Mockito.mock(RequestContext.class);
        setCurrentInstance(context);
        Mockito.doAnswer(RELEASE).when(context).release();
        return context;
    }
}
Now, a minimal JSF / Servlet environment could be set up as follows
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.faces.application.Application;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.mockito.Mockito;
import org.primefaces.context.RequestContext;

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class JsfMock implements TestRule {

    public FacesContext mockFacesContext;
    public RequestContext mockRequestContext;
    public UIViewRoot mockViewRoot;
    public Application mockApplication;
    public ExternalContext mockExternalContext;
    public HttpSession mockHttpSession;
    public HttpServletRequest mockHttpServletRequest;
    public HttpServletResponse mockHttpServletResponse;

    @Override
    public Statement apply(final Statement base, final Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                init();
                try {
                    base.evaluate();
                } finally {
                    mockFacesContext.release();
                    mockRequestContext.release();
                }
            }
        };
    }

    protected void init() {
        mockFacesContext = FacesContextMocker.mockFacesContext();
        mockRequestContext = RequestContextMocker.mockRequestContext();
        mockApplication = Mockito.mock(Application.class);
        mockViewRoot = Mockito.mock(UIViewRoot.class);
        mockExternalContext = Mockito.mock(ExternalContext.class);
        mockHttpServletRequest = Mockito.mock(HttpServletRequest.class);
        mockHttpServletResponse = Mockito.mock(HttpServletResponse.class);
        mockHttpSession = Mockito.mock(HttpSession.class);

        Mockito.when(mockFacesContext.getApplication()).thenReturn(mockApplication);
        Mockito.when(mockApplication.getSupportedLocales()).thenReturn(createLocales().iterator());

        Mockito.when(mockFacesContext.getViewRoot()).thenReturn(mockViewRoot);
        Mockito.when(mockViewRoot.getLocale()).thenReturn(new Locale("en"));

        Mockito.when(mockFacesContext.getExternalContext()).thenReturn(mockExternalContext);
        Mockito.when(mockExternalContext.getRequest()).thenReturn(mockHttpServletRequest);
        Mockito.when(mockHttpServletRequest.getSession()).thenReturn(mockHttpSession);

        Map<String, String> requestMap = new HashMap<String, String>();
        Mockito.when(mockExternalContext.getRequestParameterMap()).thenReturn(requestMap);        
    }

    private List<Locale> createLocales() {
        ArrayList<Locale> locales = new ArrayList<>();
        locales.add(new Locale("en"));
        locales.add(new Locale("de"));
        ...
        return locales;
    }
}
We mocked the most used JSF / Servlet objects, linked them with each other and provided mocks via public member variables, so that they can be extended in test classes if needed. Below is an usage example which also demonstrates how to extend the mocked objects for a particular test.
public class PaymentRequestFormTest {

    private PaymentView paymentView;

    @Rule
    public JsfMock jsfMock = new JsfMock();

    @Before
    public void initialize() {
        paymentView = mock(PaymentView.class);
        ...
    }

    @Test
    public void toJson() {
        // Mock URL and context path
        StringBuffer requestURI = new StringBuffer("http://localhost:8080/webshop");
        Mockito.when(jsfMock.mockHttpServletRequest.getRequestURL()).thenReturn(requestURI);
        Mockito.when(jsfMock.mockHttpServletRequest.getContextPath()).thenReturn("/webshop");

        // Invoke toJson method
        String json = PaymentRequestForm.toJson(jsfMock.mockFacesContext, paymentView);

        // Verify
        ...
    }
}
Any feedbacks are welcome.

Wednesday, March 5, 2014

Learning Primefaces' Extensions Development

I'm glad to announce a new book Learning Primefaces' Extensions Development. The book is about various JSF components provided in the open source, community driven project PrimeFaces Extensions - an official project with additional components to the great PrimeFaces framework. PrimeFaces is a de-facto standard framework in the Java Web Development. There are already a couple of books about the PrimeFaces core: Cookbook, Started and Beginner's Guide.

What is the new book about? The book covers most important major features of the PrimeFaces Extensions. You will meet the additional JSF components, learn step-by-step how to use them in the PrimeFaces based rich JSF applications and get some ideas for writing your own components. This is a must for every JSF / PrimeFaces developer.

The book has been written by a team member of the PrimeFaces Extensions Sudheer Jonna. Thanks Sudheer for your contribution! I'm sure PrimeFaces users will be happy to hold the book in their hands.