模拟FacesContext

时间:2010-11-23 02:57:42

标签: unit-testing jsf groovy mocking facescontext

我正在尝试将一些单元测试添加到JSF应用程序中。此应用程序并非严重依赖任何最佳实践,因此许多服务方法使用FacesContext从托管会话bean中提取数据,如下所示:

(这是在util类中)

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

嘲笑这个最好的方法是什么?我正在使用groovy所以我还有一些选项来创建我通常无法创建的类。

7 个答案:

答案 0 :(得分:12)

您可以在运行测试之前通过调用setCurrentInstance(FacesContext)通过FacesContext.getCurrentInstance返回模拟上下文。该方法受到保护,但您可以通过反射或扩展FacesContext来访问它。有一个使用Mockito here的示例实现。

答案 1 :(得分:7)

这个网址提供了一篇非常好的文章: http://illegalargumentexception.blogspot.com/2011/12/jsf-mocking-facescontext-for-unit-tests.html

你有托管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;
  }
}

你存在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;
  }
}

然后编写单元测试:

@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();
    }
  }

答案 2 :(得分:5)

您可以使用 PowerMock ,这是一个允许您使用额外功能扩展 Mockito 等模拟库的框架。在这种情况下,它允许您模拟FacesContext的静态方法。

如果您使用的是Maven,请使用以下link检查所需的依赖关系设置。

使用这两个注释注释您的JUnit测试类。第一个注释告诉JUnit使用PowerMockRunner运行测试。第二个注释告诉 PowerMock 准备模拟FacesContext类。

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

使用 PowerMock 模拟FacesContext并使用 Mockito verify(),以检查是否使用预期参数调用了resolveVariable()

@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");
}

我创建了blog post,更详细地解释了上面的代码示例。

答案 3 :(得分:3)

我举一个例子来模拟FacesConext而不使用PowerMockito。我们的想法是从Facescontext扩展一个简单的类,并使用受保护的静态方法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);
        }

    }

}

答案 4 :(得分:2)

在我的情况下,我能够在纯粹的groovy中嘲笑它。 我提供了一张可以返回的MockBeans地图:

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

答案 5 :(得分:1)

这是使用Mockito和反射模拟FacesContext的另一种方法,并确保对FacesContext.getCurrentInstance()的正常调用返回所需的(模拟的)实例:

@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();
    }
}

(这是根据@ McDowell的回答改编/扩展的。)

答案 6 :(得分:0)

我认为这里没有提供最佳解决方案。我们走了

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

您需要导入pom.xml中的所有PowerMockito软件包

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