Pruebas Una introducción práctica. Javier Gutiérrez ([email protected]) ¿Probar sirve de algo? Ariane 5. Lanzado por primera vez el 4 de junio de 1996. Ariane 5. 36.7 segundos después explotó. Motivo: Fallo software. La programación no se había probado lo suficiente. 1 ¿… y qué es una prueba?. Verificación dinámica del comportamiento del software a partir de un conjunto finito de casos de prueba. ¿… y esto qué significa?. Para probar un programa tenemos que ejecutarlo. Verificación dinámica del comportamiento del software a partir de un conjunto finito de casos de prueba. La prueba tiene un límite. No vale ejecutar el programa de cualquier manera. 2 ¿… y qué es un caso de prueba?. Valores de entrada. Probar es ejecutar un programa con unos cuantos casos de prueba. Acciones. Resultado esperado. Ejemplos de casos de prueba ¿Funciona el teléfono?. Valores de prueba 123123-4545-6767-89 Acciones 1. Descolgar auricular. 2. Marcar número de Pepote. Pepote. 3. Esperar contestación. contestación. Resultado esperado (Pepote): ”. Pepote): “Digameee “Digameee”. ¿Me está bien esta camisa? Valores de prueba Mi cuerpo. cuerpo. Acciones 1. Ponerme la camisa. camisa. 2. Abrochármela. Abrochármela. 3. Moverme un poco. poco. 4. Mirarme al espejo. espejo. Cuidado con la etiqueta o con arrugarla por si hay que devolverla Resultado esperado Elegancia y confort. confort. 3 Un ejemplo ¿Qué casos de prueba podemos escribir?. public int suma(int a, int b) { Valores de prueba Acciones Resultado esperado ??? Suma(a, Suma(a, b) ??? return a + b; } Los casos de prueba son finitos (y cuantos menos, mejor). Un ejemplo ¿Qué casos de prueba podemos escribir?. public int suma(int a, int b) { Valores de prueba Acciones Resultado esperado 0, 0 Suma(a, Suma(a, b) 0 0, b = no 0 Suma(a, Suma(a, b) b return a + b; } 3, 4 Suma(a, Suma(a, b) 7 -2, -8 Suma(a, Suma(a, b) -10 -3, 6 Suma(a, Suma(a, b) Integer.MAX_ VALUE, VALUE, 6 Suma(a, Suma(a, b) 3 -2147483643 Y algunas permutaciones más. 4 ¿Por qué he elegido esos casos de prueba?. 5 valores = 25 casos de prueba. Otro ejemplo I. public static void Intercambia(List l1, List l2) { Object b1=null; Object aux=null; Collections.sort(l1); Collections.sort(l2); ListIterator i1=l1.listIterator(); while (i1.hasNext()) { b1=i1.next(); int pos=Collections.binarySearch(l2,b1); if (pos<0) { 1 if (i1.hasNext()){ aux=i1.next(); if (b1.equals(aux)) { 2 i1.remove(); l2.add(b1); Collections.sort(l2); } } } } } Quita un elemento de la lista l1 y lo pone en la lista l2 si: 1. No está en l2. 2. Está repetido en l1. Entrada: l1 = {3, 4, 1, 2, 3, 1 } l2 = {3, 4, 2, 6, 4 } Salida: l1 = {1, 2, 3, 3 4} l2 = {1, 2, 3, 4, 4, 6} Veamos algunos casos De prueba. 5 Otro ejemplo II. Valores de prueba Acciones Resultado esperado l1 = null o l2 = null Intercambia(l1, l2) Falla !!! (Null PointerException) l1 = {} y l2 = * … L2 ordenada l1 = {1, 2, 3} y l2 = {4, 5, 6} … l1 = {1, 2, 3} y l2 = {4, 5, 6} l1 = {1, 3, 2, 3} y l2 = {4, 5, 6} … l1 = {1, 2, 3} y l2 = {3, 4, 5, 6} l1 = {1, 3, 2, 3, 1, 2} l2 = {1, 2, 3, 4, 5, 6} … l1 = {1, 1, 2, 2, 3, 3} l2 = {1, 2, 3, 4, 5, 6} * Significa una lista con cualesquiera valores. ¿Estos casos de prueba (y sus permutaciones) garantizan que el método funciona bien en cualquier circunstancia? Otro ejemplo III. Valores de prueba Acciones Resultado esperado l1 = {1, 2, 2, 3} y l2 = {4, 5, 6} Intercambia(l1, l2) Falla !!! l1 = {1, 2, 2, 3} y l2 = {4, 5, 6} l1 = {1, 1, 2, 3, 3} y l2 = {4, 5, 6} … Falla !!! l1 = {1, 2, 3, 3} y l2 = {1, 4, 5, 6} l1 = Un valor repetido 3 veces l2 = * … Falla !!! El valor aparece repetido en l1. * Significa una lista con cualesquiera valores. 6 En conclusión Probar es ejecutar casos de prueba. Caso de prueba: entrada + acciones + salida salida obtenida == salida esperada salida obtenida != salida esperada ¿Un programa que pasa todos sus casos de prueba es un programa sin errores?. JUnit I. http://junit.org Open Source framework for the automation of unit tests under Java. Existen muchas versiones para distintos lenguajes. Su API es nuestro mapa de carreteras. 7 JUnit II. IDEs: JDeveloper Eclipse Forte / Netbeans Intelli J JBuilder TogetherJ VisualAge JUnit III. Nuestra primera prueba Vamos a probar nuestra función suma import junit.framework.*; Los imports no se nos pueden olvidar. public class TestSuma extends TestCase { Cada clase guarda un conjunto de pruebas relacionadas. // Atributos // Pruebas public void testSumaPositivos() { int r = Suma.Suma(3, 4); assertEquals(“Suma de 3 y 4 no es 7", r, 7 ); } } Las clases deben heredar de TestCase. Los atributos serán la información a compartir por las distintas pruebas. Cada método que empieze por “void test…” es una prueba. Los métodos assertX() permiten evaluar si la prueba se pasa o no. 8 JUnit IV. Más cosas. Inicio y fin de la prueba. prueba. protected void setuUp(){ // Me conecto a una BBDD } protected void tearDown(){ //Me desconecto de la BBDD } void assertEquals() Métodos assertX() assertX() void assertNull() void assertNotNull() Void assertTrue() JUnit V. Nuestra segunda prueba. T.A.D. Lista (Lista.java) // Devuelve el número de elementos de la lista public int size(); // Vacía la lista. public void clear(); // Vacía la lista. public void clear(); // Añade el elemento x en la posición i. Escribir un conjunto de pruebas (JUnit) para el TAD lista. public void add(int i, Object x): // Devuelve el elemento en la posición i. public Object get(int i); 9 JUnit V. Nuestra segunda prueba. Prueba Lista (TestLista.java) protected void setUp() { listaVacia = new Lista(); listaLlena = new Lista(); elems = new Object[4]; listaLlena.add(0, elems[0] = new Integer(1)); listaLlena.add(1, elems[1] = new Integer(2)); listaLlena.add(2, elems[2] = new Integer(3)); elems[3] = new Integer(4); } En primer lugar el setUp(). Este setUp() se ejecuta siempre antes de un nuevo método test*. 2 conjuntos de pruebas: sobre una lista con elementos y sobre una lista vacía. JUnit V. Nuestra segunda prueba. 1 T.A.D. Lista (Lista.java) public void testIsEmpty() { assertTrue(listaVacia.isEmpty()); assertTrue(!listaLlena.isEmpty()); } // Devuelve el número de elementos de lapublic lista. void testClear() { public int size(); listaVacia.clear(); assertTrue(listaVacia.isEmpty()); listaLlena.clear(); assertTrue(listaLlena.isEmpty()); 2 // Vacía la lista. public void clear(); } // Devuelve true si la lista está vacía. public void testGetLlena() { public boolean isEmpty(); assertEquals(listaLlena.get(0),elems[0]); assertEquals(listaLlena.get(1),elems[1]); // Añade el elemento x en la posición i. assertEquals(listaLlena.get(2),elems[2]); public void add(int i, Object x): try { listaLlena.get(3); // Devuelve el elemento en la posición i. } catch (IndexOutOfBoundsException e) { public Object get(int i); return; } fail("Error. listaLLena no lanzó "+ “excepción indice fuera de límites"); } 3 10 JUnit V. Nuestra segunda prueba. public void testAddLlena() { listaLlena.add(3, elems[3]); assertEquals(listaLlena.get(3), elems[3]); // Devuelve el número de elementoslistaLlena.add(0, de la lista. elems[3]); assertEquals(listaLlena.get(0), elems[3]); public int size(); } T.A.D. Lista (Lista.java) 4 // Vacía la lista. public void clear(); public void testSize() { assertEquals(listaVacia.size(), assertEquals(listaLlena.size(), listaLlena.add(3, elems[0]); public boolean isEmpty(); assertEquals(listaLlena.size(), // Añade el elemento x en la posición i. listaLlena.add(4, elems[0]); public void add(int i, Object x): assertEquals(listaLlena.size(), } // Devuelve true si la lista está5 vacía. 0); 3); 4); 5); // Devuelve el elemento en la posición i. public Object get(int i); JUnit VI. Ejecución de pruebas. junit.textui.TestRunner junit.swingui.TestRunner 11 Un seguro de vida Probar mientras se codifica. Siempre que modifiquemos algo hemos de escribir una nueva prueba que lo verifique y, además, las pruebas antiguas deben funcionar. Además, mejora el diseño. Una taxonomía de casos de prueba. 12