unit testing - unit - Caras burlonasContexto
unit test microsoft (7)
Estoy tratando de agregar algunas pruebas unitarias a una aplicación JSF. Esta aplicación no se basó en gran medida en las mejores prácticas, por lo que muchos métodos de servicio utilizan FacesContext
para extraer datos de beans de sesión administrados, como por ejemplo:
(Esto es dentro de una clase util)
public static Object getPageBean(String beanReference) {
FacesContext fc = FacesContext.getCurrentInstance();
VariableResolver vr = fc.getApplication().getVariableResolver();
return vr.resolveVariable(fc, beanReference);
}
¿Cuál sería la mejor manera de burlarse de esto? Estoy usando groovy, así que tengo algunas opciones más para crear clases que normalmente no puedo crear.
Aquí hay otra forma de usar Mockito y la reflexión para simular FacesContext y asegurarse de que las llamadas normales a FacesContext.getCurrentInstance () devuelvan la instancia (simulada) que desea:
@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();
}
}
(Esto está adaptado / extendido de la respuesta de @ McDowell a continuación).
Creo que la mejor solución no se presenta aquí. Aquí vamos
@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);
}
Y necesitas importar todo el paquete de PowerMockito en tu 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>
En mi caso, fui capaz de burlarme de él en puro groovy. Proporciono un mapa de MockBeans que puede devolver:
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;
}
Esta url proporciona un artículo realmente bueno: http://illegalargumentexception.blogspot.com/2011/12/jsf-mocking-facescontext-for-unit-tests.html
Tienes tu bean gestionado:
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;
}
}
Usted apaga el 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;
}
}
Luego escribe tu prueba de unidad:
@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();
}
}
Podría usar, por ejemplo, PowerMock, que es un marco que le permite extender bibliotecas simuladas como Mockito con capacidades adicionales. En este caso, te permite FacesContext
métodos estáticos de FacesContext
.
Si está utilizando Maven, use el siguiente link para verificar la configuración de dependencia necesaria.
Anota tu clase de prueba JUnit usando estas dos anotaciones. La primera anotación le dice a JUnit que ejecute la prueba usando PowerMockRunner
. La segunda anotación le dice a PowerMock que se prepare para burlarse de la clase FacesContext
.
@RunWith(PowerMockRunner.class)
@PrepareForTest({ FacesContext.class })
public class PageBeanTest {
FacesContext
simulacro de FacesContext
utilizando PowerMock y use Verify verify()
de Mockito para verificar que se resolveVariable()
llamado a la resolveVariable()
con los parámetros esperados.
@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");
}
He creado una publicación de blog que explica el ejemplo de código anterior con más detalle.
Puede devolver un contexto simulado a través de FacesContext.getCurrentInstance
invocando setCurrentInstance(FacesContext)
antes de ejecutar la prueba. El método está protegido, pero puede acceder a él a través de la reflexión o extendiendo FacesContext
. Hay una implementación de ejemplo usando Mockito here .
Te doy un ejemplo para burlarte de FacesConext sin usar PowerMockito. La idea es extender una clase simple desde Facescontext y cambiar la instancia actual usando el método estático protegido 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);
}
}
}