Title: Pruebas unitarias en Haskell
1Pruebas unitarias en Haskell
- José Daniel Pérez Vigo
- Juan Diego Ruiz Perea
2Indice
- Introducción
- eXtreme Programming (XP)
- Test Driven Development (TDD)
- Pruebas Unitarias
- Pruebas Unitarias en otros lenguajes
- Junit
- Otros
- Pruebas unitarias en Haskell
- Hunit
- Ventajas e Inconvenientes
- Otro tipo de pruebas en haskell
- QuickCheck
- Ventajas
- Conclusiones
- Bibliografía
3Introducción
- eXtreme Programming (XP)
- Test Driven Development (TDD)
- Pruebas Unitarias
4eXtreme Programming
- La programación extrema o eXtreme Programming
(XP) fue formulada por Kent Beck, autor del
primer libro sobre la materia, Extreme
Programming Explained Embrace Change en 1996. - Se trata del proceso ágil de desarrollo de
software más famoso y también el más trasgresor y
polémico. - Es el más popular entre los MAs 38 del mercado.
5Características fundamentales
- Desarrollo iterativo e incremental pequeñas
mejoras, unas tras otras. - Pruebas unitarias continuas, frecuentemente
repetidas y automatizadas, incluyendo pruebas de
regresión. Se aconseja escribir el código de la
prueba antes de la codificación. - Programación por parejas
- Frecuente interacción del equipo de programación
con el cliente o usuario. Se recomienda que un
representante del cliente trabaje junto al equipo
de desarrollo.
6- Corrección de todos los errores antes de añadir
nueva funcionalidad. Hacer entregas frecuentes. - Refactorización del código, es decir, reescribir
ciertas partes del código para aumentar su
legibilidad y mantenibilidad pero sin modificar
su comportamiento. - Propiedad del código compartida.
- Simplicidad en el código es la mejor manera de
que las cosas funcionen.
7Test Driven Development (TDD)
- O desarrollo guiado por pruebas es una
metodología, utilizada de XP, consistente en
escribir las pruebas antes que el código. - TDD es muy efectiva gracias a la automatización
de las pruebas de programador (programmer unit
test) y al hecho de que las herramientas para
implementar está técnica son gratis y totalmente
funcionales.
8- Para que funcione el desarrollo guiado por
pruebas, el sistema tiene que ser lo
suficientemente flexible como para permitir el
testeo automático de software. - Estas propiedades permiten una rápida
retroalimentación en el diseño y la corrección.
9Pruebas unitarias
- O pruebas de unidad, consisten en comprobaciones
(manuales o automatizadas) que se realizan para
verificar que el código correspondiente a un
módulo concreto de un sistema software funciona
de acuerdo con los requisitos del sistema. - Primero escribimos un caso de prueba y sólo
después implementamos el código necesario para
que el caso de prueba se pase con éxito.
10Ventajas
- Al escribir primero los casos de prueba,
definimos de manera formal los requisitos que
esperamos que cumpla nuestra aplicación. - Al escribir una prueba de unidad, pensamos en la
forma correcta de utilizar un módulo que aún no
existe. - Los casos de prueba nos permiten perder el miedo
a realizar modificaciones en el código.
11Pruebas Unitarias en otros lenguajes
12JUnit
- JUnit es la biblioteca de pruebas de unidad
estándar de facto para Java, sus dos propósitos
básicos son ofrecer una estructura que permita
la especificación de casos de prueba, y
proporcionar un controlador capaz de llevar a
cabo las pruebas de forma automática. - Fue desarrollado por Kent Beck y Erich Gamma, y
es sin lugar a dudas la biblioteca más importante
de Java perteneciente a terceras empresas.
13- Gracias a JUnit, los códigos Java tienden a ser
mucho mas robustos, fiables, y libres de errores. - JUnit (a su vez inspirado en Smalltalk SUnit) ha
inspirado una familia entera de herramientas
xUnit, trasladando los beneficios de las pruebas
de unidad a una amplia gama de lenguajes.
14Diagrama UML de JUnit
15Ejemplo Junit Clase Divisa
- public class Divisa
-
- private int importe
- private String denominación
-
- public Divisa(int imp, String den)
- importe imp
- denominación den
-
-
- public int importe() return importe
-
- public String denominación()
- return denominación
-
-
- public Divisa sumar (Divisa d) throws Exception
- Divisa res
- if (d.denominación denominación)
16- public class DivisaTest extends TestCase
- private Divisa d5EUR, d12EUR ,
d17EUR, d8USD, expected -
- public static void main(String args)
- junit.textui.TestRunner.run(DivisaTest.class)
-
- public void setUp()
- d5EUR new Divisa(5, "EUR")
- d12EUR new Divisa(12, "EUR")
- d8USD new Divisa(8, "USD")
- d17EUR new Divisa(17, "EUR")
-
- public void testSumar()
- try
- expected (Divisa) d5EUR.sumar(d12EUR)
- /assertEqual(expected,d17EUR)/
- assertTrue(expected.equals(d17EUR))
-
- expected (Divisa) d5EUR.sumar(d5EUR)
SetUp() se ejecuta antes de cada
test TearDown() se ejecutan despues de cada test
TestSuite ts new TestSuite(DivisaTest.class) //t
s.add(otroTest) junit.textui.TestRunner.run(ts)
17Herramientas para otros lenguajes
- NUnit (.NET C,J,VB y C)
- CPPUnit (C)
- DUnit (Delphi)
- pyUnitPerf(python)
- accessUnit(Access)
- NEunit(cualquier lenguaje Unix)
- CUnit (C)
- ETester (Eiffel)
- (para ver más http//www.xprogramming.com/software
.htm)
18Pruebas unitarias en Haskell
- Hunit
- Ventajas e Inconvenientes
19HUnit
- HUnit es un framework para pruebas de unidad
realizado para Haskell, e inspirado por la
herramienta de Java JUnit. - Fue creado en 2002 por Dean Herington estudiante
graduado en la Universidad de Carolina del Norte,
en el departamento de Ciencias de la computación. - Con HUnit, como con JUnit, podemos fácilmente
crear test, nombrarlos, agruparlos dentro de
suites, y ejecutarlos con el marco de trabajo que
valida los resultados automáticamente.
20- De la misma manera que en Junit, el programador
especifica una serie de pruebas del tipo - assertEqual "nombre_prueba" ltresultadogt
ltfuncion_a_probargt - Luego se llama a la funcion que ejecuta las
pruebas - runTestTT ltpruebasgt
- Ésto muestra por pantalla los resultados de las
pruebas.
21- En el módulo donde se creen los test debemos
importar el módulo de Hunit. - Import Hunit
- Definir los casos de prueba
- test1 TestCase (assertEqual para (foo 3),"
(1,2) (foo 3) ) - test2 TestCase (do (x,y) lt- partA 3
- assertEqual para el primer
resultado," 5 x - b lt- partB y
- assertBool ("(partB " show y ") failed")
b)
22- Nombrar los test y agruparlos
- tests TestList TestLabel "test1" test1,
TestLabel "test2" test2 - Ejecutar un grupo de casos de prueba.
- gt runTestTT tests
- Cases 2 Tried 2 Errors 0 Failures 0
- gt runTestTT tests
- Failure in 0test1
- para (foo 3),
- expected (1,2)
- but got (1,3)
- Cases 2 Tried 2 Errors 0 Failures 1
23Escribiendo Test
- Los asertos (Assertions) se combinan creando
casos de prueba (TestCase), y los casos de prueba
se combinan en tests. - Hunit también provee de características avanzadas
para especificaciones de test más adecuadas.
24Asertos
- Es el bloque básico para construir test.
- data Assertion IO ()
- assertFailure String -gt Assertion
- assertFailure msg ioError (userError ("HUnit"
msg)) - assertBool String -gt Bool -gt Assertion
- assertBool msg b unless b (assertFailure msg)
- assertString String -gt Assertion
- assertString s unless (null s) (assertFailure
s)
25- assertEqual (Eq a, Show a) gt String -gt a -gt a
-gt Assertion - assertEqual preface expected actual
- unless (actual expected) (assertFailure msg)
- where msg (if null preface then "" else
preface "\n") "expected " show
expected "\n but got " show actual - Dado que los asertos son computaciones IO pueden
ser combinados usando los operadores (gtgt) y
(gtgt), y la notación do para formar asertos
colectivos. Esta combinación fallará si
cualquiera de los asertos que lo componen falla y
terminará su ejecución con el primero de los
mismos.
26Caso de Prueba(CdP)
- Es la unidad de una ejecución de prueba, es
decir, CdP distintos, son ejecutados
independientemente. El fallo de uno es
independiente del fallo de cualquier otro. - Un CdP consiste en un aserto simple o
posiblemente colectivo. Un CdP puede involucrar
una serie de pasos, cada uno terminado en un
aserto, donde cada paso debe tener éxito para
poder continuar con el CdP.
27- Para crear un CdP desde un aserto se aplica el
constructor TestCase. Ejemplos - TestCase (return ())
- TestCase (assertEqual para x, 3 x)
- Se han implementado también operadores para crear
CdP de manera mas sencilla, estos son _at_?, _at_?,
_at_? ?, ?, ? ,
28Tests
- Cuando se tiene más de un CdP se hace necesario
nombrarlos y agruparlos en listas, para ello
usamos - data Test TestCase Assertion
- TestList Test
- TestLabel String Test
- Para conocer el número de CdP que componen un
test podemos usar la función - testCaseCount Test -gt Int
29Ejecución de Pruebas
- HUnit esta estructurado para soportar múltiples
controladores de test. - Todos los controladores comparten un modelo común
de ejecución de test. Sólo difieren en como son
mostrados los resultados. - La ejecución de un test implica la ejecución
monádica de cada uno de sus CdP
30Ejecución de Pruebas (cont)
- Durante la ejecución 4 contadores sobre los casos
de prueba son mantenidos - data Counts Counts cases, tried, errors,
failures Int deriving (Eq, Show, Read) - cases número de CdP incluidos en el test.
- tried número de CdP que han sido ejecutados.
- errors número de CdP cuya ejecución termina con
una excepción no esperada. - failures número de CdP cuya ejecución termina en
fallo.
31Ejecución de Pruebas (cont)
- Tal y como procede la ejecución de una prueba,
son tres las clases de eventos de informe que se
comunican al controlador de la prueba. - start Antes de la inicialización del test, se le
manda este evento al controlador para reportar
los contadores (excluyendo el CdP actual). - error Cuando un CdP finaliza con un error, el
mensaje de error es reportado, junto con el
camino del CdP y os contadores actuales
(incluyendo el CdP actual). - failure Cuando un CdP finaliza con un fallo, el
mensaje de fallo es reportado, junto con el
camino del CdP y os contadores actuales
(incluyendo el CdP actual).
32Ventajas e Inconvenientes
- Ventajas
- La especificación de pruebas en HUnit es incluso
más concisa y flexible que en JUnit gracias a la
naturaleza del lenguaje Haskell. - El diseño del tipo Test es conciso, flexible y
conveniente para la especificación de pruebas. Es
más la naturaleza de Haskell aumenta
significativamente estas cualidades. - Combinar asertos y otro tipo de código para
construir casos de prueba es fácil con la mónada
IO. - Usando funciones sobrecargadas y operadores
especiales, la especificación de asertos y
pruebas es extremadamente compacto. - Estructurando un árbol de pruebas por valor, más
que por nombre como en JUnit, provee de una
especificación de juego de test más conveniente,
flexible y robusto. En particular, un juego de
test puede ser computado más fácilmente sobre la
marchar que en otros test frameworks. - Las facilidades de poderosa abstracción de
Haskell provee de un sopote sin igual para la
refactorización de test
33Ventajas e Inconvenientes (cont)
- Inconvenientes
- Según Diego Berrueta 1 la limitación más
importante encontrada al escribir las pruebas
unitarias consiste en la imposibilidad de
comprobar las situaciones de error debido a que
Haskell no dispone de un mecanismo común para
tratar las condiciones anormales en funciones
puras. - Otra dificultad viene determinada por la
necesidad, en muchas ocasiones, de crear
estructuras de datos complejas necesarias para su
uso en todos los casos de prueba que componen las
pruebas unitarias.
34Otro tipo de pruebas en haskell
35Quickcheck
- Fue creado por Koen Claessen y John Hughes 2
(Chalmers University of Technology) en el 2000. - Es una herramienta para el chequeo automático de
programas Haskell. - El programador proporciona una especificación del
programa a través de propiedades que las
funciones deberían satisfacer. - QuickCheck entonces prueba que las propiedades se
cumplan en un gran número de casos generados
aleatoriamente.
36- Definir una propiedad
- ltnombre_propgt ltvblesgt ltpropgt
- where ltsupuestogt ltvblegtlttipogt
- Para ejecutar la prueba de la propiedad
- quickCheck lt nombre_prop gt
- verboseCheck lt nombre_prop gt
37Propiedades
- Las propiedades son expresadas como definiciones
de funciones en Haskell, con nombres que
comienzan por prop_. - Estan universalmente cuantificadas sobre sus
parámetros. - Deben tener tipos monomórficos
- Propiedades polimórficas deben ser restringidas a
un tipo particular mediante - where supuestos (x1 t1, x2 t2, ...)
- El tipo del resultado de una propiedad debe ser
Bool, a menos que sea definida usando
combinadores que veremos más adelante.
38Propiedades Condicionales y Cuantificadas
- Las propiedades pueden tomar la forma
- ltcondicióngt gt ltpropiedadgt
- La propiedad condicional se cumplirá si para
todos los casos que la condición se cumple, la
propiedad también se cumple. - O esta otra forma
- forAll ltgeneradorgt \ltpatrongt -gt ltpropiedadgt
- Donde generador es un generador de datos de
prueba que veremos más adelante.
39Propiedades triviales
- Sirven para mostrar estadísticas del número de
casos triviales que han aparecido y tienen la
forma - ltcondicióngt trivial ltpropiedadgt
- Los casos de prueba cuya condición es True son
clasificados como triviales, y la proporción de
casos triviales sobre el total es mostrada.
40Clasificando Casos de Prueba
- Se pueden clasificar de la forma
- classify ltcondicióngt ltstringgt ltpropiedadgt
- Los casos de prueba que satisfacen la condición
son asignados a la clasificación dada, y después
del test se informa de la distribución de la
clasificación.
41Recolectando valores de datos
- Otra propiedad seria
- collect ltexpresióngt ltpropiedadgt
- El primer argumento de collect es evaluado en
cada caso de prueba, y la distribución de valores
es reportada. - Todas las observaciones vistas hasta ahora pueden
ser combinadas libremente.
42Generadores de datos de prueba Tipo Gen
- QuickCheck define generadores por defecto para
una amplia colección de datos - ..
- El programado puede usar los suyos propios con
forAll. - Los generadores tienen como tipo Gen a
- Se pueden construir mediante la función
- choose Random a gt (a,a) -gt Gen a
- do ilt-choose (0,length xs-1) return (xs!!i)
43Ventajas e inconvenientes
- Ventajas
- QuickCheck da valor a las especificaciones
ofreciendo recompensas a corto plazo. - QuickCheck fomenta formular especificaciones
precisas y formales, en Haskell. Como otras
especificaciones formales, las propiedades de
QuickCheck tienen un significado claro y no
ambiguo. - QuickCheck chequea el programa intentando
encontrar contraejemplos de sus especificaciones.
Aunque esto no puede garantizar que el programa y
la especificación son consistentes, reduce
enormemente el riesgo de que no lo sean. - Es fácil validar las especificaciones con cada
cambio que hagamos a un módulo. - Las especificaciones de QuickCheck documentan
como validar tu programa de manera que cualquier
programador que vea tu código sabrá que
propiedades han sido validadas y cuales no. - QuickCheck reduce el tiempo invertido en validar,
generando muchos CdP automaticamente.
44- Inconvenientes
- Es importante tener cuidado con la distribución
de los casos de prueba si los datos de prueba no
están bien distribuidos entonces las conclusiones
de los resultados de los test pueden no ser
válidas. - En particular el operador gt puede torcer la
distribución de los datos de prueba, ya que solo
los datos de prueba que satisfagan la condición
dada serán utilizados.
45Conclusiones
- Las pruebas unitarias son una herramienta muy
útil en el desarrollo y diseño de SW ya que
ayudan a garantizar que el programa hace justo lo
se especifica en los CdP que lo definen. - No obstante, hay algunos casos en los que no
pueden ser usadas. - Herramientas de pruebas unitarias están
implementadas en casi cualquier lenguaje de
programación.
46- Los CdP son útiles en cualquier momento del
desarrollo del SW aunque cambiemos la
implementación siempre que se mantenga la
interfaz. - Si los CdP son correctos y completos podremos
modificar sin ningún miedo nuestro código y saber
si sigue funcionando tal y como debe funcionar. - Existen otros tipos de prueba de programa además
de las pruebas unitarias.
47Bibliografía
- 1 Clases de tipo en un lenguaje
lógico-funcional, Diego Berrueta Muñoz. Tutor
Jose Emilio Labra Gayo (Universidad Politécnica
de Oviedo 2004) - http//sf.gds.tuwien.ac.at/00-pdf/z/zinc-project/m
anual-tecnico-1.0.0.pdf - Refactoring Improving the Design of Existing
Code. Martin Fowler, Kent Beck, John Brant,
William Opdyke. The Addison-Wesley Object
Technology Series.(1999) - TDD y Nunit
- www.willydev.net web sin animo de lucro con
recursos gratuitos sobre la plataforma .NET y
otros contenidos - Pruebas unitarias
- http//elvex.ugr.es web de Fernando Berzal
Galiano con software y cursos de libre
disposición. - http//www.lawebdejm.com información sobre
pruebas unitarias, CPPUnit y DUnit
48- Programación extrema (XP)
- http//www.xprogramming.com
- http//www.xprogramming.com/software.htm
encontramos software para el desarrollo de
pruebas unitarias en prácticamente todos los
lenguajes existentes. - http//www.asturlinux.org Asociación de
Usuarios Asturianos de Linux. - HUnit
- http//www.di.uniovi.es/labra Jose E. Labra.
Profesor titular de la Universidad de Oviedo.
Información sobre Pruebas Unitarias, Junit y
Hunit - http//hunit.sourceforge.net/ página principal
- http//sourceforge.net/projects/hunit sitio web
sobre el proyecto - http//www.informatik.uni-bremen.de/agbkb/lehre/ws
04-05/fmsd/ ejercicios propuestos para resolver
en hunit (universidad de bremen) - QuickCheck
- http//www.cs.chalmers.se/rjmh John Hughes
profesor de la Universidad de Tecnológica de
Chalmers en Suecia - Junit
- http//www-128.ibm.com/developerworks/java/library
/j-junit4.html - http//www.junit.org/