Mocking FacesContext

25,866

Solution 1

in my case i was able to mock it in pure groovy. i provide a map of MockBeans that it can return:

private FacesContext getMockFacesContext(def map){
        def fc = [
          "getApplication": {
            return ["getVariableResolver": {
              return ["resolveVariable": { FacesContext fc, String name ->
                return map[name]
              }] as VariableResolver
            }] as Application
          },
          "addMessage": {String key, FacesMessage val ->
            println "added key: [${key}] value: [${val.getDetail()}] to JsfContext messages"
          },
          "getMessages": {return null}
        ] as FacesContext;
        return fc;
      }

Solution 2

You can return a mock context via FacesContext.getCurrentInstance by invoking setCurrentInstance(FacesContext) before running the test. The method is protected, but you can access it either via reflection or by extending FacesContext. There is a sample implementation using Mockito here.

Solution 3

This url provides a really good article on it: http://illegalargumentexception.blogspot.com/2011/12/jsf-mocking-facescontext-for-unit-tests.html

You have your managed bean:

 package foo;

import java.util.Map;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;

@ManagedBean
@RequestScoped
public class AlphaBean {
  public String incrementFoo() {
    Map<String, Object> session = FacesContext.getCurrentInstance()
        .getExternalContext()
        .getSessionMap();
    Integer foo = (Integer) session.get("foo");
    foo = (foo == null) ? 1 : foo + 1;
    session.put("foo", foo);
    return null;
  }
}

You stub out the FacesContext:

package foo.test;

import javax.faces.context.FacesContext;

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

public abstract class ContextMocker extends FacesContext {
  private ContextMocker() {
  }

  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;
  }
}

Then write your unit test:

@Test
  public void testIncrementFoo() {
    FacesContext context = ContextMocker.mockFacesContext();
    try {
      Map<String, Object> session = new HashMap<String, Object>();
      ExternalContext ext = mock(ExternalContext.class);
      when(ext.getSessionMap()).thenReturn(session);
      when(context.getExternalContext()).thenReturn(ext);

      AlphaBean bean = new AlphaBean();
      bean.incrementFoo();
      assertEquals(1, session.get("foo"));
      bean.incrementFoo();
      assertEquals(2, session.get("foo"));
    } finally {
      context.release();
    }
  }

Solution 4

You could use for example PowerMock which is a framework that allows you to extend mock libraries like Mockito with extra capabilities. In this case it allows you to mock the static methods of FacesContext.

If you are using Maven, use following link to check the needed dependency setup.

Annotate your JUnit test class using these two annotations. The first annotation tells JUnit to run the test using PowerMockRunner. The second annotation tells PowerMock to prepare to mock the FacesContext class.

@RunWith(PowerMockRunner.class)
@PrepareForTest({ FacesContext.class })
public class PageBeanTest {

Mock FacesContext using PowerMock and use verify() of Mockito in order to check that resolveVariable() was called with the expected parameters.

@Test
public void testGetPageBean() {
    // mock all static methods of FacesContext
    PowerMockito.mockStatic(FacesContext.class);

    FacesContext facesContext = mock(FacesContext.class);
    when(FacesContext.getCurrentInstance()).thenReturn(facesContext);

    Application application = mock(Application.class);
    when(facesContext.getApplication()).thenReturn(application);

    VariableResolver variableResolver = mock(VariableResolver.class);
    when(application.getVariableResolver()).thenReturn(variableResolver);

    PageBean.getPageBean("bean_reference");

    verify(variableResolver)
            .resolveVariable(facesContext, "bean_reference");
}

I've created a blog post which explains the above code sample in more detail.

Solution 5

Here's another way to use Mockito and reflection to mock FacesContext and to make sure normal calls to FacesContext.getCurrentInstance() returns the (mocked) instance you want:

@Before
public void setUp() {

    // Use Mockito to make our Mocked FacesContext look more like a real one
    // while making it returns other Mocked objects
    ExternalContext externalContext = Mockito.mock(ExternalContext.class);
    Flash flash = Mockito.mock(Flash.class);
    FacesContext facesContext = Mockito.mock(FacesContext.class);
    Mockito.when(facesContext.getExternalContext()).thenReturn(externalContext);
    Mockito.when(externalContext.getFlash()).thenReturn(flash);

    // Use Java reflection to set the FacesContext to our Mock, since
    // FacesContext.setCurrentInstance() is protected.
    try {
        Method setter = FacesContext.class.getDeclaredMethod("setCurrentInstance", new Class[]{FacesContext.class});
        setter.setAccessible(true);
        setter.invoke(null, new Object[]{facesContext});
    } catch (Exception e) {
        System.err.println("Exception in reflection-based access to FacesContext");
        e.printStackTrace();
    }
}

(This is adapted / extended from @McDowell's answer below.)

Share:
25,866

Related videos on Youtube

mkoryak
Author by

mkoryak

Senior software engineer at Google in Cambridge. Author of jquery.floatThead - fixed table headers (1K stars on github) https://github.com/mkoryak

Updated on September 28, 2020

Comments

  • mkoryak
    mkoryak over 3 years

    I am trying to add some unit tests to a JSF application. This application didnt rely heavily on any best practices, so many service methods use the FacesContext to pull data from managed session beans like so:

    (this is inside a util class)

      public static Object getPageBean(String beanReference) {
          FacesContext fc = FacesContext.getCurrentInstance();
          VariableResolver vr = fc.getApplication().getVariableResolver();
          return vr.resolveVariable(fc, beanReference);
      }
    

    What would be the best way to mock this? I am using groovy so i have a few more options for creating classes that i cant normally create.

  • BalusC
    BalusC over 13 years
    Interesting. I might need to take a closer look to Groovy.
  • mkoryak
    mkoryak over 13 years
    the only problem with this approach is that i need to add all the methods i want to use to the mock object