TestNG

TestNG es una librería que nos permite implementar pruebas unitarias y de integración en nuestras aplicaciones Java. Es especialmente bueno para programar test concurrentes y test de estrés y es una elección más que acertada para su uso con Selenium. ¡Vamos a analizar esta estupenda herramienta!

TestNG vs JUnit

Las buenas prácticas en la implementación de test, dictan las siguientes normas:

  • Deben tener un punto de entrada conocido.
  • No deben de hacer referencia a un caso de test anterior, se deben de poder ejecutar en cualquier orden y ser atómicos.
  • Únicamente se debe probar una cosa en cada test
  • Los test deben limpiarse ellos mismos tras su ejecución

En el libro “Next Generation Java Testing” de Cédric Beust y Hani Suleiman se muestra un ejemplo muy ilustrativo sobre la filosofía de TestNG con respecto a la de JUnit. Analicemos el siguiente código. ¿Cuál será el resultado de ejecutar este test?

El resultado de ejecutar el siguiente es que los dos test funcionan correctamente y las aserciones no provocan ningún error. Esto se debe a que JUnit reinstancia la clase antes de la invocación de cada test. Digamos que JUnit se toma muy a pecho el hecho de que los test se deben de ejecutar en cualquier orden y deben ser atómicos.

Si lanzamos la misma prueba con TestNG, nos encontramos que uno de los test fallará. TestNG no reinstancia la clase antes de cada invocación de un método de test. TestNG elimina ciertas restricciones que impone JUnit en la implementación de los test. Permite definir métodos de test con cualquier nombre, que los métodos de test reciban parámetros, etc. JUnit4 toma algunas de las ideas de TestNG, aunque sigue siendo mucho menos intuitivo en algunas cosas, como por ejemplo el paso de parámetros, donde TestNG trabaja de modo más natural.

Comparativa de TestNG con JUnit4

Vamos a analizar las características de cada una de las herramientas y como se realizarían en una y otra.

Soporte a anotaciones:

  • TestNG ofrece una mayor granularidad en estos métodos de inicialización (@Before/AfterSuite, @Before/AfterTest, @Before/AfterGroup)
  • En JUnit hay que declarar “@BeforeClass” y “@AfterClass” como métodos estáticos, en TestNG no es necesario.

Testeo de excepciones:

  • TestNG: @Test(expectedExceptions = MyException.class)
  • JUnit: @Test(expected = MyException.class)

Time outs:

  • TestNG: @Test(timeOut = 1000)
  • JUnit: @Test(timeout = 1000)

Ejecución de “Suites”

Suite, es la ejecución de un grupo de clases de test en conjunto. Ambos permiten ejecutar suites, aunque de formas muy distintas:

  • JUnit: Utilizando la anotación “@RunWith” y  “@Suite”. La primera indica la clase “runner” que ejecutará los test. La segunda indica el conjunto de clases de test a ejecutar.
  • TestNG: Utiliza un fichero “testmg.xml”, donde se indican los test que se quieren ejecutar. Permite realizar agrupaciones de test por categorías, de modo que se pueda indicar el grupo de test a ejecutar.

Ignorar test:

  • JUnit: @Ignore(“No funciona este test, así que de momento no se ejecuta”)
  • TestNG:  @Test(enabled=false) también es posible indicarlo en el fichero testng.xml

Test parametrizados:

  • JUnit: Utiliza la combinación de las anotaciones “@RunWith(value = Parameterized.class)” y “@Parameter”. La primera anotación indica que se va a ejecutar un “runner” que se encargará de pasar los parámetros. El método anotado con la segunda anotación, debe devolver una lista de arrays que se utilizará para inicializar los test a través del constructor.
  • TestNG: Se pueden especificar los parámetros con un “@DataProvider”, de modo más amigable y flexible que en JUnit, o bien, también de pueden indicar en el fichero testmg.xml

Dependencia entre test:

  • JUnit: Se basa en el aislamiento de los test, no permite establecer relaciones de dependencia.
  • TestNG: Permite indicar que un test depende de otro para su ejecución. Si un test falla, el resto de test que dependan del él no se ejecutan.
    @Test(dependsOnMethods={“metodoPrincipal”})

Otras funcionalidades:

  • TestNG realiza varias acciones en su ejecución, como la generación de informes en formato HTML, generar un archivo testng-failed.xml, que permite lanzar únicamente los test que fallaron, etc.

Instalar el plugin de TestNG en Eclipse

Podemos instalar en Eclipse el plugin para TestNG desde el menú “Help>>Eclipse Marketplace”.

Instalar testng

Instalar testng

Una vez instalado podremos ejecutar test, haciendo clic derecho sobre una clase, o sobre un método, o sobre un fichero testng.XML y seleccionando “run as >> TestNG test”

Convertir test JUnit a TestNG

Desde Elclipse sobre una clase JUnit, pulsando Ctrl+1 (Quick fix), se mostrará un menú emergente que nos permitirá convertir las anotaciones. Para ello, hay que tener instalado el plugin de TestNG como hemos explicado anteriormente.

 

Paso de parámetros

Paso de parámetros desde testng.xml

El modo más simple es indicar los valores de los parámetros, en el fichero testmg.xml:

En los test, tendríamos lo siguiente:

TestNG realiza la conversión del value especificado en el xml, con los siguientes tipos:

  • String
  • int/Integer
  • boolean/Boolean
  • byte/Byte
  • char/Character
  • double/Double
  • float/Float
  • long/Long
  • short/Short

Paso de parámetros con @DataProvider

Permite pasar datos más complejos. Un “data provider”, es un método anotado con @DataProvider. Esta anotación recibe como argumento el nombre, si no se indica, será el nombre del método anotado. El data provider, devuelve objetos java, que se pasan como parámetros a los métodos de test. El test especifica el proveedor del que recibe los parámetros con el atributo “dataProvider” de la anotación test. Por ejemplo:

 

Se ejecutará el test una vez por cada lista de parámetros.

Paso de parámetros al data provider
El data provider puede recibir dos tipos de objeto, “java.lang.reflect.Method” e “org.testng.ITestContext”.  El método hace referencia al método de test a ejecutar, lo que nos permite pasar unos datos u otros en función de dicho método, por ejemplo:

Con el objeto ITestCOntext, TestNG nos pasa el contexto que está activo en ese momento, por ejemplo:

Lazy data provider
Es posible que el data provider, devuelva únicamente un resultado en cada invocación en lugar de construir de una vez toda la lista de argumentos y devolverla en una única invocación. Para conseguir esto, en lugar de Object[][], se puede devolver un java.util.Iterator. Se invocará a iterator.next() antes de la ejecución del test, para obtener los parámetros

Ejecución de test multihilo

Existen dos modos de ejecución multihilo en TestNG: “concurrent testing”, que permite la ejecución de un test múltiples veces en un entorno multihilo y “concurrent running”, que permite que en tiempo de ejecución TestNG ejecute los sus test en hilos separados para redu

Concurrent testing

Una de las principales aplicaciones de esta funcionalidad es comprobar si nuestro código es “thread safe”. Por ejemplo, tenemos el siguiente código:

Con “invocationCount” indicamos el número de invocaciones que vamos a realizar, “threadPoolSize” indica el número de hilos que se va a utilizar de manera concurrente para completar el número de invocaciones y “timeOut” indica que si alguno de los test, consume más tiempo que el indicado, terminará en fallo.

Concurrent runnig

Para ejecutar los test en paralelo, se puede indicar en el fichero “testng.xml” lo siguiente:

Indicando parallel=”methods”, cada método de test se ejecuta en su propio hilo. Si se indica parallel=”tests”, todos los métodos dentro de la etiqueta <test>, se ejecutan en su propio hilo.

Dependencias entre Tests

La idea que la ejecución de un test dependa de que se haya realizado anteriormente otro test puede chirriar a muchos desarrolladores, pero a menudo nos encontramos situaciones, en las que este comportamiento es natural. Imaginemos un test, en el que se comprueba que la aplicación está desplegada, y otros 20 test que realizan pruebas con la aplicación. Si se ejecutan en cualquier orden y fallan los 21 test, no sabemos si estos fallos se deben a errores reales, o si al no estar desplegada la aplicación han fallado todos los test.
Para indicar la dependencia de un test respecto a otros, TestNG utiliza dos atributos de la anotación @Test, “dependsOnGroups” y “dependsOnMethods”. En el primero se indica la lista de grupos de los que depende, si alguno de los métodos de test perteneciente a los grupos indicados no se ejecuta o produce fallo, el test será saltado (skip). El segundo atributo indica la lista de métodos de los que depende:

El caso de los grupos es similar, para especificar a que grupo pertenece un test, se indica del siguiente modo: @Test(groups = { “group1”, “group2” }). Estas anotaciones, si se definen a nivel de clase, se pueden heredar, ahorrando repetir la configuración en cada método.

También se pueden definir dependencias y grupos en el fichero testng.xml

El fichero testng.xml

Como se ha comentado, textng.xml contiene las suite que queremos ejecutar. Se puede ejecutar una suite del siguiente modo:

java org.testng.TestNG database-tests.xml functional-tests.xml

Los ficheros tienen esta estructura:

  • La etiqueta raíz es <suite>
  • Una <suite>  contiene una o más etiquetas <test>
  • La etiqueta <test> puede contener una o más etiquetas <classes>
  • La etiqueta <classes>  puede contener una o más etiquetas <method>

Se pueden ejecutar test JUnit en desde textmg.xml indicando junit=”true”:

Ejecución de TestNG en Maven

 

Los test programados en TestNG se ejecutan del mismo modo que los test JUnit, dentro del ciclo de vida de Maven sin necesidad de realizar ningún tipo de configuración adicional. Si queremos ejecutar los test atendiendo a la configuración del fichero testng.xml, debemos configurar el plugin surefire del siguiente modo:

Se pueden especificar los parámetros con los que queremos ejecutar la suite desde Maven, utilizando “systemPropertyVariables“:

Para ejecutar test de integración, el plugin failsafe ejecuta en la fase de test de integración los test terminados en “TestIT”, por ejempo, “UsuarioTestIT”. A parte de esto podemos indicar que se ejecute una suite, o un conjunto de clases determinado:

 

TestNG y Spring

Para cargar el contexto de test de Spring en las clases de TestNG, basta con extender de org.springframework.test.context.testng.AbstractTestNGSpringContextTests y añadir la anotación @ContextConfiguration con el/los ficheros de contexto que nos interese cargar para los tests, por ejemplo:

Como se puede ver en el ejemplo, se está cargando el contexto “src/test/resources/text-context.xml”. De este modo, podemos inyectar las dependencias que nos interese con @Autowired

Aquí terminamos el artículo. Nos dejamos cosas en el tintero, pero creo que es suficiente para conocer esta fantástica herramienta. No se trata de dar de lado JUnit, pero si necesitamos implementar test concurrentes o multihilo, condicionar la ejecución de unos test al resultado de otros, etc. TestNG nos facilita las cosas.

Si os ha gustado podéis poner un comentario, recordad que los comentarios son moderados y no se muestran automáticamente en la página. Podéis leer más artículos sobre testing aquí

 

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *