Mockear la misma clase que se esta testeando para realizar pruebas unitarias

En el artículo anterior comenté que es aconsejable aislar en diferentes métodos aquellos fragmentos de código que no pueden ser testeados unitariamente (por ejemplo las consultas con Hibernate Criterias, o Hql) de tal manera que puedan ser mockeados y de esa manera poder testear el resto del código.

Pero cuando intenté llevar a la práctica esto, descubrí que no quería crear una clase nueva para tener estos métodos, sino que quería que estén en la misma clase. Esto me planteaba un problema. Necesitaba mocker el método que no quería que se ejecutara, pero no sabía si podía mockear una clase en la cual había un método que si quería testear.

La respuesta fácil es SI, se puede, pero hay una limitación. El método que se quiere mockear hay que hacerlo utilizando metaClass. No se puede utilizar demand, como con los mocks normales, sino que se debe si o si utilizar metaClass.

Uno puede decir, que para eso no hago ningún mock, y simplemente utilizo metaClass para pisar el método en cuestión, pero lamentablemente, como dicen en muchos otros blogs, hacer eso no es seguro, y puede afectar el resultado de otros tests. (de hecho en mi se veía el error en los test de integración que comenzaron a fallar y no entendía por qué)

Basta con llamar a mockFor sobre la clase que se quiera testear, y luego se puede utilizar metaClass tranquilamente para cambiar esa clase, que cuando se termine el test los cambios se revertirán normalmente.

Se que mucho tal vez no se entienda, asi que dejaré un pequeño ejemplo comentado aquí

Quiero testear el método agregar de la clase DestacadoService. Este método internamente llama al método buscarDestacado el cual utiliza bastante hql para realizar su función, el cual no funciona en pruebas unitarias, entonces para realizar el test de agregar se necesita hacer algo similar a lo siguiente:

mockFor(DestacadoService)
DestacadoService.metaClass.buscarDestacado = { pais, tipo, id -> return null; }

def service = new DestacadoService()
def r = service.agregar(pais.argentina, tipo.principal, 'mi destacado')

Como se puede apreciar, en la línea 1 llamamos a mockFor sobre la clase que queremos mockear
En la línea 2, utilizamos metaClass para cambiar el método (y retornar algo estático que no use hql ni nada)
En la siguiente línea de código instanciamos la clase que contiene el método que queremos testar
Y finalmente, en la última lo ejecutamos

Haciendo esto nos aseguramos que luego del test el cambio sobre la clase causado por la modificación del metaClass sea revertido y no afecte al resto de los tests.

Exactamente lo mismo se debe hacer cuando se quieren modificar métodos que estén en clases de dominio. Solo que en lugar de utilizar mockFor se puede utilizar mockDomain


One Response to “Mockear la misma clase que se esta testeando para realizar pruebas unitarias”

  1. […] Sin embargo a veces no queremos poner estos métodos en otra clase, sino que deseamos que sean métodos de la misma clase que estoy estoy testeando, o los métodos se encuentran en clases de dominio, y ahí se producen algunos otros problemillas que trataré en el siguiente post. […]

Discussion Area - Leave a Comment