CONTENIDO
PÁG
Introducción Verificación y Validación....................................
5
Testing y Vocabulario Básico..............................
6
Testing de Software............................................
7
Estrategias de Testing.........................................
7
Tipos de Tests…………….......................................
8
Tests Unitarios....................................................
9
Características de los Tests.................................
9
Importancia de los Tests Unitarios....................
10
Checklist de un Test Unitario…………………………
10
Venus.Js.............................................................
12
PHPUnit………………………………………………………
12
CppUnit ……………………………………………………….
13
JUnit……………………………………………………………
13
Simple Test…………………………………………………..
14
Prueba de Método (Ejemplo) ………………………..
14
Referencias Bibliográficas.……………………………
21
EDICIÓN-DIRECCIÓN Brimar Rodríguez brimar.rodriguez.2101@gmail.com
DISEÑO Juan Peñalver juan280274@hotmail.com
EDITORIAL Curso Especial de Grado Universidad de Oriente Núcleo de Monagas
INTRODUCCIÓN Los tests unitarios proporcionan un incremento en la productividad, esto permite que los códigos sean mucho más limpios, manejables y presentan menos bugs. Son muchas las críticas que reciben los Test-Driven Development, y esto descalifica el hecho que un programador practique esta técnica. Estas críticas tienen que estar basadas en el desconocimiento de lo que supone esta metodología. Y esto viene de lo quizá antiintuivo del método: Escribir un test previo a cualquier código. Escribir el mínimo código necesario para que el test se supere. Refactorizar en cada iteración. Es cierto que cuesta aplicar en un principio este método, pero funciona; aunque no siempre el resultado es el esperado. Porque, una de las cosas que nunca debemos olvidar es que, tanto el éxito como el fracaso de este sistema, radica en su elemento más pequeño: los tests unitarios. Y ese es el tema principal de esta revista.
E
l testing de software pertenece a una actividad o etapa del proceso de producción de software denominada Verificación y Validación.
V&V es el nombre genérico dado a las actividades de comprobación que aseguran que el software respeta su especificación y satisface las necesidades de sus usuarios. El sistema debe ser verificado y validado en cada etapa del proceso de desarrollo utilizando los documentos (descripciones) producidas durante las etapas anteriores. En rigor no solo el código debe ser sometido a actividades de V&V sino también todos los subproductos generados durante el desarrollo del software. Por ejemplo, en otros capítulos hemos estudiado cómo verificar un modelo formal utilizando un asistente de pruebas. Otro ejemplo es la verificación de la arquitectura y el diseño. En efecto, estos subproductos deben ser verificados de forma tal de que exista mayor confianza en que cumplen con los requerimientos del cliente –en particular el equipo de desarrollo debe asegurarse de que la arquitectura podrá incorporar los cambios previstos a bajo costo y que esta habilita otras propiedades que haya solicitado el cliente tales como seguridad, portabilidad, etc.; para más detalles sobre la verificación de arquitecturas se puede consultar. Tareas semejantes deben llevarse a cabo para los otros subproductos del desarrollo. Si bien estos términos en su uso cotidiano pueden llegar a ser sinónimos, en Ingeniería de Software tienen significados diferentes y cada uno tiene una definición más o menos precisa. Validación: ¿estamos construyendo el producto correcto? Verificación: ¿estamos construyendo el producto correctamente?
5
En este sentido, la verificación consiste en corroborar que el programa respeta su especificación, mientras que validación significa corroborar que el programa satisface las expectativas del usuario
por las semiformales (el testing es la más conocida pero ciertas técnicas de análisis estático de código se usan frecuentemente), hasta la prueba formal de programas, el cálculo de refinamiento, etc.
En otras palabras, la verificación es una actividad desarrollada por ingenieros teniendo en cuenta un modelo del programa y el programa en sí, en tanto que la validación la debe realizar el usuario teniendo en cuenta lo que él espera del programa y el programa en sí.
Aunque el testing se puede aplicar tanto en verificación como en validación, en este curso nos concentraremos casi exclusivamente en el testing de programas como parte de la verificación. Es decir que no estudiaremos otras técnicas de verificación, no estudiaremos técnicas de validación y no estudiaremos la V&V de otras descripciones más allá del programa.
Existen varias técnicas dentro del marco de la V&V desde las más informales (prototipación de requerimientos, revisión de requerimientos, etc.), pasando
Testing y Vocabulario Básico El testing es una actividad desarrollada para evaluar la calidad del producto, y para mejorarlo al identificar defectos y problemas. El testing de software consiste en la verificación dinámica del comportamiento de un programa sobre un conjunto finito de casos de prueba, apropiadamente seleccionados a partir del dominio de ejecución que usualmente es infinito, en relación con el comportamiento esperado. Es una técnica dinámica en el sentido de que el
6
Definición programa se verifica poniéndolo en ejecución de la forma más parecida posible a como ejecutará cuando esté en producción –esto se contrapone a las técnicas estáticas las cuales se basan en analizar el código fuente. El programa se prueba ejecutando solo unos pocos casos de prueba dado que por lo general es física, económica o técnicamente imposible ejecutarlo para todos los valores de entrada posibles programa es correcto (perfecto).
Testing de Software El testing de software es el arte de medir y mantener la calidad del software para asegurar que las expectativas y requerimientos del usuario, el valor de negocio, los requisitos no funcionales como la seguridad, confiabilidad y
tolerancia a fallos, y las políticas operacionales se cumplan. El testing es un esfuerzo del equipo para conseguir un mínimo de calidad y tener una “definición de hecho” entendida y aceptada por todos.
Estrategias de Testing Caja Negra
Caja Gris Caja Blanca
El contenido de la caja (la implementación de la solución) es oscura. Los testers sólo se centran en la entrada y en la salida, normalmente cuando se realizan test de sistema o de aceptación de usuario.
El contenido de la caja es visible y se puede analizar como parte del testing.
Es una combinación de caja blanca y negra que se usa normalmente en casos especiales, en los que se requiere una comprensión de cómo funciona por dentro y cómo debe comportarse
7
Tipos de Tests
Estrategia
8
Descripción
Test exploratorio
El tester imagina posibles escenarios que no se hayan cubierto por otros test. Es muy útil cuando se observa al usuario usando el sistema. NO hay test predefinidos.
Test de integración
Testear diferentes componentes de la solución trabajando como si fueran uno.
Test de carga
Testear cómo se comporta el sistema con cargas de trabajo en un entorno controlado
Test de Regresión
Asegura que el sistema mantiene los mínimos de calidad después de hacer cambios como corregir bugs. Usa una mezcla de tests unitarios y de sistema.
Smoke Test
Se usa para testear una nueva característica o idea antes de subir al repositorio de código los cambios
Test de sistema
Testea el sistema completo con características fijas y comprueba los requerimientos no funcionales.
Test unitario
Un test de la unidad más pequeña de código (método, clase, etc.) que puede ser testeada de manera aislada del sistema.
Test de aceptación de usuario
Cuando se acerca el final de los ciclos del producto, se invita a los usuarios a que realicen test de aceptación en escenarios reales, normalmente basados en casos de tests
Tests Unitarios Las pruebas unitarias aseguran que un único componente de la aplicación produce una salida correcta para una determinada entrada. Este tipo de pruebas
validan la forma en la que las funciones y métodos trabajan en cada caso particular. Las pruebas unitarias se encargan de un único caso cada vez, lo que significa que
un único método puede necesitar varias pruebas unitarias si su funcionamiento varía en función del contexto.
Características de los Tests Unitarios Para que una prueba unitaria tenga la calidad suficiente se deben cumplir los siguientes requisitos:
Automatizable No debería requerirse una intervención manual. Esto es especialmente útil para integración continúa.
Repetibles o Reutilizables No se deben crear pruebas que sólo puedan ser ejecutadas una sola vez. También es útil para integración continua.
Independientes La ejecución de una prueba no debe afectar a la ejecución de otra.
Completas Deben cubrir la mayor cantidad de código.
Profesionales Las pruebas deben ser consideradas igual que el código, con la misma profesionalidad, documentación, etc.
Aunque estos requisitos no tienen que ser cumplidos al pie de la letra, se recomienda seguirlos o de lo contrario las pruebas pierden parte de su función.
9
Importancia de los Tests Unitarios Cuando alguien pregunta por qué son importantes los tests unitarios, le solemos preguntar si creen que es importante que un avión comercial que usan habitualmente para cruzar océanos sea testeado meticulosamente antes de cada vuelo. ¿Es importante que el avión sea testeado? Si, ok, ahora, ¿es importante que el altímetro sea testeado aparte? Por supuesto. Eso es el test unitario… testear el altímetro. No garantiza que el avión vaya a volar, pero no puede volar de forma segura sin él.
La importancia de los test unitarios, empieza con el proyecto y continúa durante todo el ciclo de vida de la aplicación, también depende de si estamos buscando: Código de calidad desde el principio. Menos bugs. Código auto explicativo. Reducir el coste de corregir errores encontrándolos lo antes posible. Sentido de responsabilidad del código, estar orgullosos de nuestro código.
Checklist de un Test Unitario Check Convención de nombres descriptivos
10
Descripción Establece una convención de nombres descriptivos como ClassName_Purpose_ExpectedResult, consiguiendo un estilo de tests consistente y con una intención clara. Ejemplos: Credentials_UserNameLength_Succeed() Credentials_UserNameLength_Fail()
Documenta los tests
Los test unitarios prueban y validan características de negocio. El propio código es una documentación viva del sistema. Recomendamos el uso combinado de código conciso y documentación mínima, para conseguir un test unitario entendible, mantenible y autodescriptivo.
Errores y aserciones descriptivas
Usa mensajes descriptivos para mejorar la lectura del código y el log de compilación. Ejemplos: Content submission failed due to too many spelling errors.
Adopta el desarrollo contra interfaces Mantenlo simple
Mantenlo centrado
La interfaz define el contrato, permitiendo el uso de stubs y tests de caja negra. El test unitario debe ser tan simple, limpio y conciso como sea posible. Los tests simples son un valor añadido, si no, se ignorarán y no se mantendrán Un test unitario está centrado en la mínima unidad de código (método, clase, etc.) que puede ser probada de manera aislada, no a nivel de sistema o de integración. Para mantener un test y su infraestructura asociada centrada, evita probar cosas entre los diferentes niveles de aplicación o de sistema.
Tiene una sola aserción lógica
Cada test debe tener sólo una aserción lógica. Debe validar el test, como se indica en su nombre. Una aserción lógica puede contener una o varias sentencias de assert.
Organiza y mantén los tests
El código debe ser organizado y mantenido como si fuese una clase de primer nivel, al igual que el resto de la solución. Su código debe basarse en los mismos requisitos de buenas prácticas, calidad y estilo de código de la compañía.
Testea los casos buenos, malos y los límite
El test unitario debe cubrir todos los posibles escenarios y busca siempre una amplia cobertura de código. Testear los casos límites y las excepciones es responsabilidad del testador, ¡no del usuario final!
11
Venus.Js Es una herramienta flexible para ejecutar test unitarios en Javascript. Ha sido desarrollado por Linkedin como aplicación node.js. Elimina las tareas repetitivas permitiéndonos centrarnos en escribir test en lugar de preocuparnos en su ejecución. Los principales requisitos que se fijó Linkedin en el momento de crear esta herramienta, primeramente de uso interno para complementar los test de integración de Selenium, fueron los siguientes: Soporte para múltiples librerías de test como Mocha, QUnit o Jasmine, pensando en posibles librerías futuras. Para ello se han definido adaptors para cada una y se ha definido
una forma concisa de hacer lo propio con futuras librerías que aparezcan. Soporte para TDD e integración continua, usando navegadores actuales. Escribir test debe tener la menor fricción posible entre sí. Los tests unitarios deben poder ser mejorados y ampliados lo más fácilmente posible. Debido a la inconsistencia entre muchos navegadores, especialmente con las APIs del DOM. Venus.js soporta múltiples entornos de testing con navegadores actuales: Chrome, Firefox, Internet Explorer y Safari.
PHPUnit Es un entorno para realizar pruebas unitarias en el lenguaje de programación PHP. PHPUnit es un framework de la familia
12
xUnit. Se creó con idea de que cuanto antes se detecten los errores en el código antes podrán ser corregidos. Como todos los frameworks de
pruebas unitarias, PHPUnit utiliza assertions para verificar
que el comportamiento de una unidad de código es el esperado.
CppUnit Es un marco de programación de pruebas unitarias para el lenguaje de programación C++. El proyecto ha sido bifurcado en diversas ocasiones. La versión de
freedesktop.org, mantenida por Markus Mohrhard, programador del proyecto LibreOffice, está en desarrollo activo y es distribuida en varias distribuciones Linux como openSUSE, Debian, Ubuntu, Gentoo y Arch Linux.
JUnit Es un conjunto de bibliotecas creadas por Erich Gamma y Kent Beck que son utilizadas en programación para hacer pruebas unitarias de aplicaciones Java. JUnit es un conjunto de clases (framework) que permite realizar la ejecución de clases Java de manera controlada, para poder evaluar si el
funcionamiento de cada uno de los métodos de la clase se comporta como se espera. Es decir, en función de algún valor de entrada se evalúa el valor de retorno esperado; si la clase cumple con la especificación, entonces JUnit devolverá que el método de la clase pasó exitosamente la prueba; en caso de que el valor esperado sea diferente al que regresó el método durante la ejecución, JUnit devolverá un fallo en el método correspondiente.
13
Simple Test SimpleTest es un framework de pruebas unitarias de código abierto para el lenguaje de programación PHP. La estructura de la prueba es similar a JUnit / PHPUnit. SimpleTest soporta objetos simulados y se
puede utilizar para automatizar las pruebas de regresión de aplicaciones web con un cliente HTTP scripts que puede analizar las páginas HTML y simular cosas como hacer clic en los enlaces y enviar formularios.
Prueba del Método Supondremos que tenemos una clase que tiene un método suma. Este método, recibe como parámetro dos valores enteros y devuelve la suma ambos. Sí, el ejemplo es un poco tonto pero la idea es dar a conocer la herramienta, tiempo habrá para complicar el ejemplo.
También se puede hacer desde el menú Prueba → Nueva prueba. Una vez implementado el método necesitamos probarlo. Para ello creamos nueva primera prueba unitaria. Si seleccionamos el método Sumar, en el menú contextual tendremos la opción de crear una prueba unitaria. (Ilustración 1).
14
La prueba la podremos crear en un nuevo proyecto de test o en uno ya existente. Como es nuestra primera prueba, lógicamente, lo incluiremos en un nuevo proyecto de test. (Ilustración 2). Crearemos al menos un proyecto de Test por cada proyecto que tengas en tu aplicación, en lugar de crear un único proyecto de test que englobe todos los las pruebas de tu aplicación.
15
El nuevo proyecto que se crea es un proyecto normal y corriente. La única peculiaridad que tiene una referencia de Microsoft.VisualStudio.QualityTools.UnitTestFramework. Si nos fijamos en el Explorador de soluciones podremos ver el nuevo proyecto que se ha agregado a nuestra solución. (Ilustración 4) En este paso no se crea la prueba, sólo la estructura de proyectos, clases y métodos. Es labor de desarrollador, en base a las especificaciones del módulo, el desarrollo de las pruebas. Es importante que el desarrollador realice tantas pruebas como sea necesario para cubrir al máximo la funcionalidad del módulo que se prueba.
Fijense en el código del método de prueba que tanto la clase como el método son clases y métodos normales, con la única peculiaridad que el método está decorado con el atributo TestMethod.
16
Cuando hemos creado el proyecto de Test, en la solución han aparecido dos nuevos ficheros: Sample.vsmdi y LocalTestRun.testrunconfig. Si hacemos doble-click sobre el archivo vsmdi podremos acceder al editor de la lista de pruebas unitarias. (Ilustración 5). Desde aquí podremos ver, lanzar o depurar todas las pruebas unitarias que contiene la solución. Y digo que contiene la solución, porque este fichero es común para todos los proyectos de Test de la solución. Si éste contiene 2 proyectos de Test, desde la lista de pruebas unitarias veremos todas las pruebas de los dos proyectos.
El fichero LocalTestRun.testrunconfig lo que nos va a permitir es configurar algunos aspectos relacionados con la ejecución de las pruebas, como activar el análisis de la cobertura de código. (Ilustración 6)
17
Una vez que tenemos la estructura de nuestro método de prueba tendremos que implementar la prueba. La idea es muy sencilla. Si pasamos 1 y 2 en los parámetros de entrada el método sumar nos tiene que devolver 3. Es decir, sabiendo los parámetros de entrada y lo que hace el método que estamos probando, sabremos lo que tiene que devolver. El objetivo de la prueba es comprobar que el método Sumar hace exactamente lo que se espera de él. Si el método Sumar en lugar de 3 devolviese 7, porque se ha equivocado al sumar, la prueba unitaria fallaría. Una vez hecha prueba recuerda eliminar la sentencia Assert.Inconclusive. Esta sentencia se incluye cuando se crea la prueba e indica que el resultado de la prueba no es concluyente. De esta manera se marcan las pruebas que no están implementados.
Una vez, hecha la prueba sólo tenemos que ejecutarla. Desde la ventana de lista de pruebas podemos ejecutar todas las pruebas o sólo
18
aquellas que seleccionemos. En la parte inferior, en la ventana de resultados de la prueba veremos el resultado de la ejecución de los mismos. (Ilustración 7)
En la prueba puedes ver cómo usando la sentencia Assert.AreEqual se comprueba si el resultado del método Sumar corresponde con el resultado esperado. La clase Assert es parte del Framework y que se incluye dentro de la referencia Microsoft.VisualStudio.QualityTools.UnitTestFramework. Esta clase nos proporciona algunos métodos para comprobar los resultados de los métodos que están sometidos a la prueba; IsFalse, IsNull, AreSame, etc Parece por tanto, que toda prueba debería terminar con una cláusula de este tipo para comprobar si la prueba ha sido correcta o no. Pues bien, esto no es así. Aunque seguramente usaremos alguno de los métodos de la clase Assert en la mayoría de las situaciones no es de uso obligatorio. Si no usamos esta cláusula, la ejecución de la prueba dará un resultado correcta siempre y cuando la ejecución de la misma no genere una excepción. Por ejemplo, vamos a modificar nuestro método suma para que sólo sume valores positivos y si alguno de los parámetros que recibe es menor que 0, que devuelva una excepción.
19
En este caso, nos podría interesar hacer una prueba dónde comprobemos que no se genera una excepción cuando los parámetros sean mayores que 0. En esa prueba no sería necesario incluir ninguna sentencia de comprobación. Si le pasamos valores positivos la prueba será correcta siempre y cuando no genera una excepción. Del mismo modo, nos interesará hacer una prueba para comprobar que la validación se hace correctamente y que se devuelve la excepción ArgumentException en este caso . En este caso, deberemos decorar el método de prueba con el atributo ExpectedException. Con este atributo estamos especificando que el resultado esperado de la ejecución de la prueba es la excepción ArgumentException. Si no se genera la excepción o la excepción no es del tipo esperado, la prueba fallará.
20
REFERENCIAS BIBLIOGRテ:ICAS Prueba unitaria. (2015, 15 de marzo). Wikipedia, La enciclopedia libre. Fecha de consulta: 01:18, abril 8, 2015 desde http://es.wikipedia.org/w/index.php?title=Prueba_unitaria&oldid=8 0603799. Potencier, F., & Weaver, R. (s.f.). LibrosWeb. Recuperado el 6 de Abril de 2015, de http://librosweb.es/libro/symfony_2_x/capitulo_10/tests_unitarios. html Provost, P., & Weber, J. (2013). Microsoft Developer Network. Recuperado el 6 de Abril de 2015, de https://msdn.microsoft.com/eses/library/dn449640.aspx Rodriguez, T. (24 de Junio de 2013). Genbeta:dev. Recuperado el 6 de Abril de 2015, de http://www.genbetadev.com/javascript/venus-js-herramienta-opensource-para-ejecutar-tests-unitarios-en-javascript PHPUnit. (2014, 14 de enero). Wikipedia, La enciclopedia libre. Fecha de consulta: 01:52, abril 8, 2015 desde http://es.wikipedia.org/w/index.php?title=PHPUnit&oldid=7189828 6 CppUnit. (2015, February 8). In Wikipedia, The Free Encyclopedia. Retrieved 01:57, April 8, 2015, from http://en.wikipedia.org/w/index.php?title=CppUnit&oldid=64611174 4 JUnit. (2015, 6 de febrero). Wikipedia, La enciclopedia libre. Fecha de consulta: 02:05, abril 8, 2015 desde http://es.wikipedia.org/w/index.php?title=JUnit&oldid=79868227. SimpleTest. (2014, May 1). In Wikipedia, The Free Encyclopedia. Retrieved 02:09, April 8, 2015, from http://en.wikipedia.org/w/index.php?title=SimpleTest&oldid=60666 8449
21