Mocking FacesContext

前端 未结 7 1102
执念已碎
执念已碎 2021-02-18 19:20

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

相关标签:
7条回答
  • 2021-02-18 19:45

    I believe the best solution is not presented here. Here we go

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({ FacesContext.class})
    public class MyTestClass{
    
    @Mock
    private FacesContext facesContext;
    
    @Before
    public void init() throws Exception {
            PowerMockito.mockStatic(FacesContext.class);
            PowerMockito.when(FacesContext.getCurrentInstance()).thenReturn(facesContext);
    }
    

    And you need to import all the PowerMockito bundle in your pom.xml

            <dependency>
                <groupId>org.mockito</groupId>
                <artifactId>mockito-all</artifactId>
                <version>${mockito.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.powermock</groupId>
                <artifactId>powermock-api-mockito</artifactId>
                <version>${powermock.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.powermock</groupId>
                <artifactId>powermock-module-junit4</artifactId>
                <version>${powermock.version}</version>
                <scope>test</scope>
            </dependency>
    
    0 讨论(0)
  • 2021-02-18 19:46

    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();
        }
      }
    
    0 讨论(0)
  • 2021-02-18 19:47

    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;
          }
    
    0 讨论(0)
  • 2021-02-18 19:53

    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.)

    0 讨论(0)
  • 2021-02-18 19:59

    I give you an example to mock FacesConext without using PowerMockito. The idea is to extend a simple class from Facescontext, and change the current instance using protected static method setCurrentInstance:

    import javax.faces.context.FacesContext;
    import javax.servlet.ServletContext;
    
    import org.junit.Before;
    import org.junit.Test;
    import org.mockito.Mock;
    import org.mockito.MockitoAnnotations;
    
    import com.sun.faces.config.InitFacesContext;
    
    public class DummyTest {
    
        @Mock
        private FacesContext context;
    
        @Before
        public void before(){
            MockitoAnnotations.initMocks(this);
            ServletContext sc = mock(ServletContext.class);
            new FakeContext(sc);
            assertEquals(context, FacesContext.getCurrentInstance());
        }
    
        @Test
        public void dummy(){
    
        }
    
        private class FakeContext extends InitFacesContext{
    
            public FakeContext(ServletContext sc) {
                super(sc);
                setCurrentInstance(context);
            }
    
        }
    
    }
    
    0 讨论(0)
  • 2021-02-18 20:00

    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.

    0 讨论(0)
提交回复
热议问题