9

我很难在我的 JSF 支持 bean 类上实现单元测试......例如,一些方法使用会话或请求参数,使用这种代码获得:

FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("paramKey");.

我的问题是:如何测试从会话或请求中获取值的方法?

4

2 回答 2

9

我通常做的是避免将静态方法调用到我要测试的 bean 中。这意味着要重构您当前的代码:

FacesContext.getCurrentInstance().getExternalContext()
    .getSessionMap().get("paramKey");

有没有办法用它们的静态方法调用来测试它们?可能有,但他们给我带来的麻烦多于帮助。所以最后我摆脱了它们并改变了我的设计。只需让第二个 bean 来完成(稍后您将对其进行模拟)。在您的情况下,创建一个@SessionScoped管理该功能的 bean:

@ManagedBean
@SessionScoped
public class SessionBean{

    public Object getSessionParam(String paramKey){
        FacesContext.getCurrentInstance().getExternalContext()
           .getSessionMap().get(paramKey);
    }

}

并将该 bean 注入每个需要它的 bean 中(我通常从具有它的抽象 bean 扩展我的视图/请求 bean,因此不必在每个 bean 中实现它):

@ManagedBean
@RequestScoped
public class RequestBean{

    @ManagedProperty(value="#{sessionBean}")
    private SessionBean sessionBean;

    public void accessSessionParam(){
        sessionBean.getSessionParam("name");
    }

}

这样您就可以通过您的辅助轻松访问静态方法SessionBean。那么,如何测试呢?只需创建它的模拟(例如使用Mockito):

public class Test{

    public void test1(){
        SessionBean sb = Mockito.mock(SessionBean.class);
        //Mock your 'getSessionParam' method
        ValueBean vb = new ValueBean();
        Mockito.when(sb.getSessionParam(Mockito.anyString()).thenReturn(vb);
        //Create your bean to test and set your mock to it
        RequestBean rb = new RequestBean();
        rb.setSessionBean(sb);
        //here you can test your RequestBean assuming 
        //sessionBean.getSessionParam() 
        //will return vb for every single call
    }

}
于 2013-07-28T21:01:27.890 回答
6

可以模拟FacesContext,但这并不理想。模拟示例:

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

如果您的平台支持它,那么首选CDI而不是 JSF 托管 bean。CDI 具有静态依赖检查并注入代理以防止范围泄漏。CDI 不支持 JSF 的所有特性,但在必要时将 JSF 托管 bean 连接到 CDI相对容易。

JSF 托管 bean 限制了您注入类型的范围,但即使这样也可能导致范围泄漏。例如,#{sessionScope}即使对象属于请求范围,变量也可以注入到会话范围 bean 中ExternalContext。可以通过编写自己的 JSF bean 代理来克服这个问题。

注意:大部分内容是在考虑 Java EE 6 的情况下编写的;Java EE 7 可能会有所改善。

于 2013-07-28T21:33:48.860 回答