19

我正在尝试向 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,所以我有更多选项来创建我通常无法创建的类。

4

7 回答 7

16

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

于 2012-01-26T17:11:49.813 回答
11

这个网址提供了一篇非常好的文章: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();
    }
  }
于 2014-02-26T21:23:44.233 回答
6

例如,您可以使用PowerMock,它是一个允许您扩展Mockito等具有额外功能的模拟库的框架。在这种情况下,它允许您模拟FacesContext.

如果您使用的是 Maven,请使用以下链接检查所需的依赖项设置。

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

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

FacesContext使用PowerMockverify()进行模拟并使用Mockito来检查是否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");
}

我创建了一篇博文,更详细地解释了上述代码示例。

于 2014-11-09T19:56:13.847 回答
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);
        }

    }

}
于 2014-11-19T10:24:47.970 回答
3

这是使用 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 下面的回答改编/扩展的。)

于 2017-09-27T22:09:53.517 回答
2

就我而言,我能够用纯正的方式来模拟它。我提供了一张可以返回的 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;
      }
于 2010-12-02T14:13:18.443 回答
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>
于 2016-09-22T19:27:07.673 回答