Poder para nuestros Tests

In Novedades by Onetec

Cualquiera que haya escrito tests unitarios se ha visto antes o después con el clásico problema de no poder aislar el comportamiento de diferentes objetos que trabajaban juntos. Muy a menudo este problema puede resolverse usando inyección de dependencias, lo que de paso reduce el acoplamiento, y aumenta por tanto la capacidad del sistema de adaptarse a cambios. Es un efecto secundario verdaderamente agradable.

No obstante no siempre es posible, o deseable, cambiar el diseño para hacer pruebas unitarias, y para estos casos voy a hablar de PowerMock, un framework que nos permitirá dar solución a algunos de los problemas más recurrentes que aparecen cuando intentamos aislar comportamiento.

 

Un ejemplo típico es cuando nos encontramos con una llamada a un método estático de otra clase. Puesto que no tenemos una instancia no podemos inyectar nada y el test dependerá de lo que pase dentro de esa otra clase. Nuestro SUT (Subject Under Test) no está aislado, y no podemos probarlo de forma unitaria. En otras palabras, el test podría fallar y nuestra clase no tener la culpa de nada porque alguno de los objetos que utiliza internamente no está haciendo lo que debería.

Un subejemplo de este ejemplo es cuando trabajamos con fechas y llamamos a Calendar.getInstance(). En este caso sabemos que Calendar.getInstance() funciona bien, entre otras cosas porque los responsables de ese código han escrito sus propias pruebas unitarias. Si nuestro test falla podemos estar razonablemente seguros de que no es porque Calendar no haga bien su trabajo. A pesar de eso Calendar.getInstance() nos devolverá un objeto Calendar con la fecha y hora de la llamada, lo cual es un problema si queremos probar que se obtiene un determinado resultado, ya que cada vez que ejecutemos el test será en un tiempo diferente.

 

Ejemplo:

public class ClasePrueba {

    public static Date fechaDeAyer() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE,-1);
        return calendar.getTime();
    }
}

 

Dado que la instancia se obtiene dentro del método, no tenemos ningún control sobre la fecha creada. El resultado de fechaDeAyer() siempre será distinto, ya que depende del momento en el que se ejecute, y eso supone un problema si queremos compararlo con un resultado predefinido “correcto” para automatizar las pruebas. Pero ¿qué pasaría si pudiéramos controlar desde fuera lo que va a devolver Calendar.getInstance() dentro de nuestro SUT? Ahí es donde entra la magia de PowerMock:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ ClasePrueba.class })

public class ClasePruebaTest {

    private final Calendar treceEnero = new GregorianCalendar(1979,1,13);
    private final Calendar doceEnero = new GregorianCalendar(1979,1,12);

    @Test
    public void fechaDeAyer_devuelve_la_fecha_de_ayer(){

        mockStatic(Calendar.class);
        when(Calendar.getInstance()).thenReturn(treceEnero);

        Date resultado = ClasePrueba.fechaDeAyer();
        assertEquals(doceEnero.getTime(), resultado);
    }
}

Ahora podemos decirle a nuestro test que el resultado esperado es el 12 de enero porque podemos forzar a que nuestro SUT, sin tocar su implementación, se ejecute siempre en el 13 de enero.

Estas y otras capacidades de PowerMock lo hacen especialmente idóneo para su uso en desarrollo de pruebas unitarias en Liferay ya que el conocido framework de portales está literalmente plagado de clases de utilidad con métodos estáticos. Todas estas llamadas podemos simularlas a nuestro antojo aislando lo que queremos probar, con la implementación real, de lo que no queremos probar, usando mocks.

PowerMock extiende la API de Mockito o de EasyMock, y funciona con JUnit o TestNG. Más información en su página de github:

https://github.com/jayway/powermock