Inicio > Java > Usando Mockito en Test U

Usando Mockito en Test U

Esta semana tuve la oportunidad de probar el framework Mockito para la realización de Test U. Mockito me ayudó mucho ya que los métodos que tenía que testear necesitaban un juego de datos que era muy complicado a agregar en la BDD H2 que se utiliza para los otros tests. De hecho mi superior me dijo que tenía dos opciones, o pasar varias horas agregando los datos a la BDD de test (nada práctico) o “mockear” los métodos de las clases de la capa de Datos que eran llamados por la clase a testear.

Entonces me puse en la tarea de saber qué es “mockear”. Pues bueno, se trata solamante de simular el comportamiento de ciertos objetos. Por ejemplo, podemos mockear una interfaz que no ha sido aún implementada. En las interfaces sabemos cuáles son sus métodos y lo que éstos deben retornar en los diferentes escenarios, pero no sabemos cómo lo hacen.

Ejemplo

Supongamos un ejemplo simple:

Queremos testear la funcionalidad addBook, de la cuál disponemos la implementación. Digamos que la regla para la inserción de un nuevo Libro es que el libro no exista, es decir que no haya otro libro con el mismo título y el(los) mismo(s) autores. Si el libro ya existe, una IllegaStateException debe ser lanzada.

Para verificar ésto, la interfaz BookDAO nos propone un método que permite buscar libros existentes tomando como parámetros palabras claves relacionadas con el título. Como no hay implementación de esta clase, pero ya sabemos cuál es el comportamiento, es la clase que debe ser mockeada.

El método addBook es el siguiente:

@Override
public void addBook(Book book) throws IllegalStateException {
___List existingBooks = bookDAO.findBooksByTitle(book.getTitle().split(" "));
___if( existingBooks.isEmpty() ){
______bookDAO.store(book);
___} else {
______for( Book tempBook : existingBooks ) {
_________if( tempBook.getAuthors().containsAll(book.getAuthors()) ) {
____________throw new IllegalStateException("Already existing book");
_________}
______}
______bookDAO.store(book);
___}
}

Ahora bien. La gracia de utilizar Mockito en los Test U es de imaginar los diferentes escenarios en los que puede ser ejecutada una funcionalidad. Para el caso actual tenemos 3 escenarios:

  • Escenario 1. El libro no existe, entonces addBook es ejecutado y el libro es agregado con éxito.
  • Escenario 2. El libro no existe, tiene el mismo título de un libro que existe pero un autor diferente. El libro puede ser agregado, y addBook es ejecutado.
  • Escenario 3. El libro ya existe. La excepción es lanzada.

Inicialización del Test U y declaración del Objeto a Mockear

Para iniciar mi Test U, debo entonces indicar qué clase voy a mockear. De la misma maneras voy a suponer un juego de datos. Para esto en la clase de Test LibrarySearchServiceTest, agrego un @MethodBefore para inicializar mis objetos.


LibrarySearchServiceImpl service = new LibrarySearchServiceImpl();

@Mock
private BookDAO bookDAO;

private Book existingBook;
private Author author1;
private Book newBook;
private Author author2;


@BeforeClass
public void init_Mockito_mocks(){
___MockitoAnnotations.initMocks(this);
___service.setBookDAO(bookDAO);
}


@BeforeMethod
public void initDataSet(){
___author1 = new Author("Maximo Gorki");
___author1 = new Author("Pearl S. Buck");
___existingBook = new Book("La madre", author1);
___newBook = new Book("La madre", author2);
}

@Mock, permite entonces inicializar el objeto en el framework Mockito, para poder indicarle cómo comportarse según los llamados que va a recibir.

Test Escenario 1 :

“El libro no existe, entonces addBook es ejecutado y el libro es agregado con éxito.”

Supongamos que vamos a guardar newBook. En ese caso, debemos simular que cuando findBooksByTitle es llamado teniendo como parámetros “La” y “Madre”, el método devuelve una lista vacía.

Para esto, incluimos la clase Mockito de manera estática. – De esta manera, los métodos están disponibles en LibrarySearchServiceTest sin necesidad de hacer Mockito.metodo(…). –

import static org.mockito.Mockito.*;

Para simular el comportamiento de findBooksByTitle, el método when de Mockito es utilisado. La sintaxis es muy sencilla de comprender:

when(objeto_mockeado.metodo_a_simular(parametros_especificos)).then(resultado_a_simular)

Al final el test queda de esta manera:

@Test
public void test_addBook_with_success() {
___//given
___when(bookDAO.findBooksByTitle(anyString())).thenReturn( new ArrayList<Book>() );
___//when
___service.addBook(newBook);
___//test
___verify(bookDAO, times(1)).store(newBook);
}

Fijémonos que en la lista de parámetros, se utiliza el método anyString() de Mockito, que permite simular que, no importa el valor del parámetro que se le pase (en este caso, no importa que palabras claves le pasemos), siempre devolverá una lista vacía.

El test es completado con el llamado al método verify. Como su nombre lo indica, permite verificar que un método del objeto mockeado fue ejecutado. En el caso del test, verificamos que store es ejecutado una vez teniendo como parámeto newBook.

Test Escenario 2 :

“El libro no existe, pero tiene el mismo título de un libro que existe. El libro puede ser agregado, y addBook es ejecutado.”

Supongamos que existingBook ya existe y que vamos a agregar newBook. En ese caso, debemos simular que cuando findBooksByTitle es llamado teniendo como parámetros “La” y “Madre”, el método devuelve existingBook. Como sabemos que el libro de todas maneras debe ser guardado, porque los autores son diferentes, entonces verificamos que store fue ejecutado una vez.

@Test
public void test_addBook_when_newBook_has_already_existing_title_but_different_Author_with_success() {
___//given
___when(bookDAO.findBooksByTitle("La", "madre")).thenReturn( Lists.newArrayList( existingBook ) );
___//when
___service.addBook(newBook);
___//test
___verify(bookDAO, times(1)).store(newBook);
}

Test Escenario 3 :

“El libro ya existe. La excepción es lanzada.”

Supongamos que existingBook y newBook ya existen. Un nuevo Book llamado alreadyExistingBook con el mismo título y el mismo autor de existingBook sera agregado. Debemos simular que cuando findBooksByTitle es llamado teniendo como parámetros “La” y “Madre”, el método devuelve existingBook y newBook. La validación sobre el autor nos debera retornar la excepción, que sera validada por el atributo expectedException que se agrega a la anotacion @Test de testng.

@Test ( expectedExceptions= IllegalStateException.class )
public void test_addBook_should_throw_IllegalStateException_when_newBook_has_already_exists() {
___//given
___when(bookDAO.findBooksByTitle("La", "madre")).thenReturn( Lists.newArrayList( existingBook, newBook ) );
___Book alreadyExistingBook = new Book("La madre", author1);
___//when
___service.addBook(alreadyExistingBook);
}

Más información

Bueno, para los interesados en ver como funciona completamente el código del ejemplo que expongo, pueder hacer click acá ou hacer un git clone desde https://github.com/jomaora/test-mockito.git, branch master. El código está en un proyecto maven. Para completar la información con respecto a la utilización de Mockito: para agregar Mockito en un proyecto con maven, solo agregamos la dependecia en el pom.xml:

<dependency>
___<groupId>org.mockito</groupId>
___<artifactId>mockito-core</artifactId>
___<version>${mockito.version}</version>
</dependency>

Y sino, pues obviamente ir a la página de Mockito -> http://code.google.com/p/mockito/

O la página de uno de mis colegas de trabajo, Brice Dutheil, que es además uno de los commiters del proyecto Mockito, y gracias a quién pude empezar a conocer la herramienta. A él, “Buen trabajo!”.

Categorías:Java Etiquetas: ,
  1. 23 febrero, 2012 a las 14:15

    Muy buena herramienta, buen post

  1. No trackbacks yet.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Hype Driven Development

coz' geeks love new stuff !

My experiments with SCRUM

Site to discuss Agile (Scrum, XP, etc) concepts and ideas.

CommitStrip

Mi propia cheatsheet...

Chris Aniszczyk's (zx) diatribe

work. life. open source. diatribes.

GermanTrevi

repositorio de mi mente...

A %d blogueros les gusta esto: