Pruebas unitarias en Android con Mockito

jueves, 20 de noviembre de 2014



Ya había escrito un artículo sobre pruebas unitarias con moq, que es una librería para .Net. En esta ocasión vamos a ver como podemos hacer lo mismo en Android utilizando Mockito.

Introducción a Mockito

Mockito es un libería open source que nos va a permitir crear objetos dobles, pudiendo así realizar pruebas unitarias de objetos con dependencias de forma clara y simple.

En Android Studio vamos a necesitar añadir la dependencia a mockito-core pero si además quieres ejecutar los test sobre el dispositivo o emulador cómo es nuestro caso ya que vamos a utilizar test de android basandonos en AndroidTestCase, vamos a necesitar la librería dexmaker también. Las dependecias las añadimos en el fichero build.gradle asi:
    
androidTestCompile 'org.mockito:mockito-core:1.9.5',
                   'com.google.dexmaker:dexmaker:1.0',
                   'com.google.dexmaker:dexmaker-mockito:1.0'

Ejemplo

Vamos a realizar el mismo ejemplo que habíamos visto en el artículo sobre pruebas unitarias con moq para .Net.

Vamos a tener un BlogService con un método publishPost y va a tener dos dependencías que se le inyectan en el constructor, un Logger y un HtmlValidator. En caso de validar correctamente el html a publicar, escribe en el log y devuelve true, si el html es incorrecto escribe en el log el error y lanza una exepción.

Veamos los componentes

public interface Logger {
    public void log (String message);
}

public interface HtmlValidator {
    boolean isValid(String html);
}

public class BlogService {
    Logger logger;
    HtmlValidator htmlValidator;

    public BlogService(Logger logger, HtmlValidator htmlValidator){
        this.logger = logger;
        this.htmlValidator = htmlValidator;
    }

    private void Log(String message){
        if (logger != null)
            logger.log(message);
    }

    public boolean publishPost(String html){
        if (htmlValidator.isValid(html))
        {
            Log("Post has published");

            return true;
        }
        else
        {
            Log("publish error, html not valid");

            throw new IllegalArgumentException("html not valid");
        }
    }
}


Testeando con un Dummy

Una primera prueba unitaria es simplemente crearnos las dependencias sin implementación, a modo de objetos dummy para verificar que no hay ningún problema al crear el servicio de blog.

public class BlogServiceTest extends AndroidTestCase {

    public void testCreateInstance() {
        Logger loggerMock = mock(Logger.class);
        HtmlValidator htmlValidatorMock = mock(HtmlValidator.class);

        BlogService blog = new BlogService(loggerMock, htmlValidatorMock);

        Assert.assertNotNull(blog);
    }
}    

Testeando con un Stub

La siguiente prueba unitaria es comprobar que cuando le pasamos un html incorrecto, el servicio de blog lanza una IllegalArgumentException. Preparamos un stub para el interface HtmlValidator que siempre devuelva false en el método isValid y en el test con un try catch verificamos que la excepción es lanzada en este caso.

public void testPublishPostShouldThrowExceptionForNotValidHtml() {

    try {
        Logger loggerMock = mock(Logger.class);

        HtmlValidator htmlValidatorMock = mock(HtmlValidator.class);
        when(htmlValidatorMock.isValid(any(String.class))).thenReturn(false);

        BlogService blog = new BlogService(loggerMock, htmlValidatorMock);

        blog.publishPost("html html html");

        Assert.fail("Expected IllegalArgumentException");
    } catch (IllegalArgumentException expectedException) {
        String expectedMessage = "html not valid";
        Assert.assertTrue("error message should contain message " + expectedMessage,
                expectedException.getMessage().contains(expectedMessage));
    }
}

Testeando con un Mock

En la siguiente prueba vamos a verificar el caso contrario, que todo va bien y no se produce ninguna excepción, además vamos a verificar el servicio de blog realiza una llamada a nuestro objeto doble HtmlValidator, lo que lo convierte en un mock.

public void testPublishPostShouldReturnTrue() {
    Logger loggerMock = mock(Logger.class);

    HtmlValidator htmlValidatorMock = mock(HtmlValidator.class);
    when(htmlValidatorMock.isValid(any(String.class))).thenReturn(true);

    BlogService blog = new BlogService(loggerMock, htmlValidatorMock);

    boolean published = blog.publishPost("valid html");

    verify(htmlValidatorMock).isValid(any(String.class));
    Assert.assertTrue("Excepted return true in publishPost method",published);
}


En el metodo verify estamos verificando que se ha invocado la función isValid y con la función any indicamos que el parámero da igual para la verificación, puede ser cualquier valor. Si hubieramos especificado un html concreto lo que estariamos verificando es que se haya llamado a la función isValid con un parámetro concreto. La función verify también tiene un segundo parámetro que es times, por defecto es times(1) y significa que estamos validando el numero de veces que se ha invocado la función o método. Por defecto su valor es uno.

Código disponible en GitHub.

Libros relacionados

Mockito Cookbook

Practical Unit Testing with JUnit and Mockito
Mastering Unit Testing Using Mockito and JUnit

2 comentarios: