我正在尝试将一些单元测试添加到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所以我还有一些选项来创建我通常无法创建的类。
答案 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>