prueba cubrimiento

UD3. Diseño y realización de pruebas

1. Procedimientos de pruebas y casos de pruebas.
2 Tipos de pruebas, funcionales, estructurales, regresión.
3 Pruebas de caja blanca y caja negra.
4 Herramientas de depuración de código. JUnit.
5 Planificación de Pruebas.
6 Calidad del software.

1 Procedimientos de pruebas y casos de pruebas.

Los procedimientos de prueba es la definición del objetivo que se desea conseguir con las pruebas y qué es lo que se va a probar y cómo.

El objetivo de las pruebas no siempre es detectar errores. Muchas veces lo que se quiere conseguir es que el sistema ofrezca un rendimiento determinado, que el interfaz tenga una apariencia y cumpla unas características determinadas, etc.

Por lo tanto la ausencia de errores en las pruebas nunca significa que el software las pase. Hay muchos parámetros en juego.

Cuando se diseñan los procedimientos se deciden las personas que hacen las pruebas y bajo qué parámetros se van a realizar las mismas.

No siempre tienen que ser los programadores los que hacen las pruebas. Siempre tiene que haber personal externo al desarrollo puesto que los propios programadores por experiencia sabemos que probamos solamente las cosas que funcionan. Si supieran los programadores dónde están los errores los corregirían ¿No crees?

Hay que tener en cuenta que es imposible probar todo, la prueba exhaustiva no existe. Muchos errores del sistema saldrán en producción cuando el software ya está implantado pero se intentará siempre que sea el mínimo número de ellos.

En los planes de pruebas (es un documento detallado) generalmente se cubren los siguientes aspectos:

  • Introducción. Breve introducción del sistema describiendo objetivos, estrategia, etc.
  • Módulos o partes del software a probar. Detallar cada uno de estas partes o módulos.
  • Características del software a probar. Características individuales o conjuntos de ellas.
  • Características del software a no probar.
  • Enfoque de las pruebas. En el que se detallan entre otros las personas responsables, la planificación, la duración, etc.
  • Criterios de validez o invalidez del software. En este apartado se registra cuando el software puede darse como válido o como inválido especificando claramente los criterios.
  • Proceso de pruebas. Se especificará el proceso y los procedimientos de las pruebas a ejecutar.
  • Requerimientos del entorno. Incluyendo niveles de seguridad, comunicaciones, necesidades hardware y software, herramientas, etc.
  • Homologación o aprobación del plan. Este plan deberá estar firmado por los interesados o responsables del mismo.
  • Las demás fases del proceso de pruebas como se puede entender son el mero desarrollo del plan de pruebas anterior.

    Recuerda.
    Es prácticamente imposible realizar pruebas exhaustivas a un programa. Son demasiado costosas. Salvo que el programa sea tan importante como para realizarlas, lo que se hace es llegar a un punto intermedio en el cual se garantiza que no va a haber defectos importantes o muchos defectos y la aplicación está completamente operativa con un funcionamiento aceptable.

    Edsger Dijkstra.

    dijkstra

    dijkstra

    Excelente científico holandés que destacó por sus algoritmos. Daba soluciones a problemas de una forma sólida y eficiente. Entre sus algoritmos destacan el algoritmo de Dijkstra, el problema de la cena de los filósofos, los comandos guardados, el algoritmo del banquero o el de shunting yard.

    Una de sus frases quedará para la historia “las pruebas sólo pueden demostrar la presencia de errores, no su ausencia”. Como Dijkstra sabía bien, cuando se prueba un software pueden pasarse por alto defectos o errores que saldrán más adelante o incluso nunca se llegarán a conocer.

    El objetivo de las pruebas es el de convencer tanto a los usuarios como a los propios desarrolladores que el software es lo suficientemente robusto como para poder trabajar con él de forma productiva.

    Cuando un software supera unas pruebas exhaustivas, las probabilidades de que ese software de problemas en producción se atenúan de manera que la fiabilidad del mismo aumenta.

    Dimas habla con Manuela y le dice. ¿Sabías que mi profesor de ciclos nos habló un día de Edsger Dijkstra?
    Manuela se quedó con una cara como de sorpresa.
    ¿No sabes quién es Dijkstra? Le dijo Dimas. Pues lo que puedes hacer es mirarte el problema de la cena de los filósofos y el OSPF. Que sepas que el OSPF utiliza el algoritmo de Dijkstra.

    Los casos de pruebas

    En la fase de pruebas se diseñan y preparan los casos de prueba. Un caso de prueba se crea con el objetivo de encontrar fallos.

    Por experiencia, decir que no hay que probar los programas de forma redundante. Si se prueba un software y funciona la mayoría de las veces no hace falta probar lo mismo. Hay que crear otro tipo de prueba, no repertirlas.

    Tenemos que tener en cuenta que la prueba no debe ser muy sencilla ni muy compleja. Si es muy sencilla no va a aportar nada y si es muy compleja quizás sea difícil encontrar el origen de los errores.

    Recuerda.
    Probar es ejecutar casos de prueba uno a uno pero el que un software pase todos los casos de prueba no quiere decir que el programa esté exento de fallos.

    Como hemos visto, las pruebas solo encuentran o tratan de encontrar aquellos errores que van buscando, luego es muy importante realizar un buen diseño de las pruebas con buenos casos de prueba puesto que se aumenta de esta manera la probabilidad de encontrar fallos.

    Recuerda.
    Un caso de prueba es un conjunto de entradas, condiciones de ejecución y salidas esperadas diseñadas para un objetivo concreto.
    login
    Ejemplo de caso de pruebas:

    Imaginemos que tenemos la ventana anterior y queremos realizar un caso de pruebas. La descripción de un caso de pruebas CASO 1 para esta aplicación sería el siguiente:

    Objetivo:
    Comprobar que un usuario correcto entra al sistema y la fecha y hora de entrada queda registrada.

    Entrada de datos:
    En el campo user se introduce “myfpschool” y en el campo password “Troconne77″.

    Condiciones:
    En la tabla usuarios existe el usuario “myfpschool” con la password “Troconne77″ encriptada en MD5.

    Resultado:
    El usuario entra en el sistema y en la tabla log deja un registro (“myfpschool”,fecha,hora).

    Procedimiento de la prueba:

  • Se comprueba en la tabla usuarios que existe el usuario a introducir.
  • Se comprueba que la password esté codificada en MD5 correctamente.
  • En los campos user y password se teclean los datos “myfpschool” y “Troconne77″.
  • Se pulsa aceptar y se comprueba que se accede al sistema correctamente.
  • Se revisa la tabla log y se comprueba que se ha registrado el usuario, la fecha y la hora actual.
  • Codificación y ejecución de las pruebas

    Una vez diseñados los casos de prueba hay que generar las condiciones necesarias para poder ejecutar dichos casos de prueba. Habrá que codificarlos en muchos casos generando set o conjuntos de datos. En estos set de datos hay que incluir tanto datos válidos, datos inválidos o incluso algunos datos fuera de rango o disparatados.

    El beta-testing.
    Muchas empresas de videojuegos, software, etc, ofrecen una primera versión o release a una comunidad determinada de usuarios para que la prueben. El objetivo de esta versión beta es descubrir el mayor número de errores posibles. Es más fácil (y más barato) el descubrir errores por parte de decenas, centenas o miles de usuarios que por un reducido número de ingenieros de calidad.

    También habrá que preparar las máquinas sobre las que se van a hacer las pruebas instalando el software necesario, los usuarios de sistema, realizar carga del sistema, etc.

    Ejecución de las pruebas

    Una vez definidos los casos de prueba y establecido el entorno de las pruebas es el momento de la ejecución de las mismas.

    Se irán ejecutando los casos de prueba uno a uno y en el momento que se detecte algún error, lo que hay que hacer es aislarlo y anotar la acción que se estaba probando, el caso, el módulo, la fecha, hora, los datos utilizados, etc. De esa manera se intentará documentar lo más detalladamente posible el error.

    En el caso que se produzcan errores aleatorios también hay que registrarlos anotando este hecho.

    Emma le explica a Dimas y Manuela que es importante probar el software y que las pruebas las haga una persona distinta al que programó el código.

    Andrino habla también con Dimas y con Manuela de las pruebas de software y les da un consejo:

    Casi siempre que se encuentra un error en un módulo de software, si se insiste, se encontrarán más“.


    2 Tipos de pruebas, funcionales, estructurales, regresión.

    Ya conocemos qué son las pruebas y qué objetivos tienen las mismas. En cuanto al tipo de pruebas a realizar existen muchas categorías. Veremos las más frecuentes.

    En primer lugar tenemos las pruebas funcionales. Las pruebas funcionales como su nombre indican buscan que los componentes software diseñados cumplan con la función con la que fueron diseñados y desarrollados. Estas pruebas buscan lo que el sistema hace más que cómo lo hace.

    Todos los sistemas tienen una serie de funcionalidades o características y esas son las que se van a testear.

    El tester para la realización de las pruebas se basa en la documentación existente (manual de usuario y otros manuales). También se suelen realizar pruebas en conjunto con los usuarios puesto que ellos saben cómo tiene que funcionar el sistema.

    Estas pruebas se suelen considerar como pruebas de caja negra. No se evalua cómo el sistema funciona internamente pero sí qué es lo que hace.

    También existen otro tipo de pruebas como pueden ser las pruebas de seguridad en las que se evalúan aspectos de seguridad o pruebas de interoperabilidad cuando existen interfaces entre el sistema y otros. Se prueba la compatibilidad entre el sistema y los demás sistemas con los que interactúa.

    Las pruebas no funcionales son pruebas más técnicas realizadas al sistema. Estas pruebas suelen seguir siendo de caja negra puesto que nunca se examina la lógica interna de la aplicación.

    Suelen ser pruebas no funcionales las pruebas de carga, pruebas de estrés, pruebas de rendimiento, pruebas de fiabilidad, etc.

    Entre las pruebas que examinan de forma más detallada la arquitectura de la aplicación tenemos las pruebas estructurales. Estas pruebas son de caja blanca puesto que en algún momento de las mismas se utilizan técnicas de análisis del código.

    Generalmente para este tipo de pruebas se utilizan herramientas especializadas.

    Otro tipo de pruebas son las pruebas de regresión o las pruebas repetidas. No se suele probar lo ya probado pero en el caso que el software haya sido modificado generalmente se realizan este tipo de pruebas.

    Este tipo de pruebas intentan descubrir si existe algún error tras las modificaciones o en algún caso si se encuentra algún tipo de problema que no se había descubierto previamente.

    Solamente se realizarán estas pruebas en el caso de una modificación de software.

    En muchos casos este tipo de pruebas no sirven para descubrir nuevos errores sino para certificar la ausencia de ellos durante las mismas. Ten en cuenta que el que no aparezcan errores durante estas pruebas no significa que el software este exento de los mismos.

    Este tipo de pruebas de regresión suelen automatizarse y se agrupan en conjuntos llamados Regression test suites (conjuntos de pruebas de regresión).


    3 Pruebas de caja blanca y caja negra.

    En las pruebas de caja blanca se conoce o se tiene en cuenta el código que se quiere probar. Se denomina también “clear box testing” porque la persona que realiza las pruebas está en contacto con el código fuente.
    Su objetivo es probar el código, cada elemento del mismo.

    Existen algunas clases de pruebas de este tipo como por ejemplo:

    - Pruebas de cubrimiento.
    - Pruebas de condiciones.
    - Pruebas de bucles.

    Veamos en qué consisten cada una de estas pruebas:

    Caja blanca. Pruebas de cubrimiento.

    En este tipo de pruebas el objetivo es ejecutar al menos una vez todas las sentencias o líneas del programa.

    En ocasiones es imposible cubrir el 100% del código porque puede haber condiciones que nunca se cumplan:

    if (a > 20 && a < 10) { .... }

    O también puede haber excepciones o notificaciones de error en un código que nunca va a fallar (código sin errores).

    El número ciclomático.
    El número ciclomático es el que indica el número de circuitos que existen en un grafo (circuito). Indica el número de maneras que hay de ir desde un nodo hasta el mismo sin pasar dos veces por el mismo arco.

    Para realizar las pruebas habrá que generar el suficiente número de casos de prueba para poder cubrir los distintos caminos independientes del código. En cada condición deberá cumplirse en un caso y en otro no.

    prueba cubrimiento

    En la figura anterior podemos ver cómo una función determinada para averiguar si nuestra novia es un poco friki puede generar un grafo con varias posibilidades.

    Para hacer la prueba de cubrimiento tenemos dos opciones, la primera es crear casos de pruebas para ejecutar las líneas de código al menos una vez, y por lo tanto los conjuntos de datos de pruebas ({videogames=0,manga=0,technology=0},{videogames=1,manga=0,technology=0},{videogames=1,manga=1,technology=0},{videogames=1,manga=1,technology=1}) serían válidos.

    Si lo que queremos hacer es comprobar todas y cada una de las combinaciones de caminos el número de casos de prueba se dispara. No quizás en esta función que es pequeña pero imaginemos un programa más extenso.

    Caja blanca. Pruebas de condiciones.

    En este caso necesitaremos varios casos de prueba. En una condición puede haber varias condiciones simples y tendremos que generar un caso de pruebas por cada operando lógico o comparación. La idea es que en cada expresión se cumpla en un caso y en otro no.

    Veamos un ejemplo:

    if (videogames=1 && manga=1 && technology=1){ freaky = 1}

    En el caso anterior deberemos comprobar todas y cada una de las combinaciones de las tres variables anteriores. En este caso son variables pero podrían ser otro tipo de expresiones más complejas.

    De esa manera nos cercioramos que cualquiera de las combinaciones de valores de la condición funcionará tal y como el programa fué concebido.

    Caja blanca. Pruebas de bucles.

    Los bucles son estructuras que se basan en la repetición, por lo tanto la prueba de bucles se basará en la repetición de un número especial de veces.

    En el caso de un bucle simple los casos de prueba deberían contemplar lo siguiente:

  • Repetir el máximo, máximo -1 y máximo +1 veces el bucle para ver si el resultado del código es el esperado.
  • Repetir el bucle cero y una vez.
  • Repetir el bucle un número x de veces.
  • En el caso de bucles anidados tendremos bucles internos y externos. Sería bueno realizar la prueba de bucles simple para los bucles internos ejecutando el bucle externo un número determinado de veces y luego realizar la prueba contraria. El bucle interno se ejecuta un número determinado de veces y el externo se prueba con las pruebas anteriores de bucle simple.

    Dimas y manuela están pensando en realizar pruebas de caja blanca para un proyecto que están diseñando. Se quieren poner de acuerdo en cómo hacer las pruebas de bucles para su código. Lo que van a hacer entre los dos es crear los casos de prueba para una función concreta y de esa manera tener una idea general de cómo lo van a hacer para el resto del programa.

    int factorial ( int input )
    {
    int x, fact = 1;
    for ( x = input; x > 1; x–)
    fact *= x;

    return fact;

    }

    Ayúdales tú a generar los casos de prueba para la función anterior.

    Entre las pruebas de caja negra (aquellas que simplemente prueban la interfaz sin tener en cuenta el código) podemos citar las siguientes:

    - Pruebas de cubrimiento.
    - Pruebas de clases de equivalencia de datos.
    - Pruebas de valores límite.

    Caja negra. Pruebas de cubrimiento.

    Manuela siempre ha tenido una duda con el tema de las pruebas de cubrimiento y le pregunta a Emma. ¿Por qué las pruebas de cubrimiento se pueden considerar de caja blanca y caja negra?

    Emma se queda pensando la pregunta para intentar responderle de la mejor manera posible y le dice. Cubrimiento en caja blanca quiere decir ejecutar todas las líneas de código mientras que en caja negra significa probar todas las funciones o funcionalidades de la aplicación.

    En una prueba de cubrimiento tienes que probar todas las opciones y te puedes ayudar del manual de usuario u otro manual técnico que explique cada una de las funciones del interfaz. Ojo el interfaz.

    Caja negra. Pruebas de clases de equivalencia de datos.

    login
    Imaginemos que estamos probando un interfaz y debemos generar un código de usuario y una clave.

    El sistema nos dice que el código de usuario tendrá que tener mayúsculas y minúsculas, no puede tener caracteres que no sean alfabéticos y tiene que tener al menos 6 letras (máximo 12). Las password tendrán al menos 8 caracteres (máximo 10) y contendrán letras y números.

    Para testear este interfaz lo que debemos hacer es establecer clases de equivalencia para cada uno de los campos. Deberemos crear clases válidas y clases inválidas por cada uno de los campos. Por ejemplo:

    Usuario
    - Clases válidas: “Pelegrino” “Rocinante”
    - Clases inválidas: “marrullero44″ “nene” “Portaavionesgigante” “Z&aratustra” “*Ventajoso12″

    Password
    - Clases válidas: “5Entrevias” “s8brino”
    - Clases inválidas: “corta” “muyperoquemuylarguisima” “oletugarbo” “999999999″

    El objetivo de esta prueba es probar todas las clases válidas y las inválidas al menos una vez. Cada vez que diseñemos un caso de prueba con datos inválidos introduciremos solamente una clase inválida. De esa manera conoceremos si el programa esta funcionando correctamente. Muchas veces al utilizar varias clases inválidas los errores se enmascaran y no podemos conocer si todas las clases funcionan.

    Caja negra. Pruebas de valores límite.

    Este tipo de pruebas son complementarias a las pruebas de particiones. El objetivo es generar valores que puedan probar si el interfaz y el programa funcionan correctamente. Imaginemos que entramos a la página web de un banco para testearla y el interfaz cuando vamos a transferir una cantidad nos dice: “La cifra máxima que usted puede transferir hoy es de 10.000 euros”.

    Si queremos probar dicha interfaz nosotros probaríamos por ejemplo valores fuera de rango como -100 o 20.000, también valores en los límites como 0,1,9.999,10.000,10.001 o valores típicos e intermedios como 9.000 o 2.500.

    El objeto de esta prueba está en que muchas veces los programadores se equivocan al establecer los límites en la frontera (se equivocan y ponen < en vez de <= por ejemplo).


    4 Herramientas de depuración de código. JUnit.

    En NetBeans es fácil utilizar JUnit. JUnit es un framework que permite realizar test repetibles, eso quiere decir que podemos diseñar un test para un programa/clase concreta y ejecutarlo tantas veces como sea necesario. La ventaja es que podemos ejecutar el test cada vez que se modifique o cambie algo del código y verificar si el programa tras los cambios sigue funcionando correctamente.

    Para ejecutar JUnit en Netbeans necesitaremos que nuestro entorno de desarrollo sea versión 7.2 o superior y que nuestro JDK sea también versión 7 o superior. En caso contrario deberemos actualizar a una versión superior el software.

    Creando una primera clase de test

    El primer paso para crear una clase de test es tener una primera clase a testear.

    En nuestro caso hemos creado un nuevo proyecto desde File -> New Project.

    junit1

    Luego hay que elegir en la categoría Java “Java Class Library”.

    junit2

    Para el nombre del proyecto hemos elegido “ejemplo_junit” y le hemos elegido la ubicación donde queremos que se guarde. En el caso que esté seleccionada la opción “Use dedicated folder” la deseleccionamos.

    junit3

    El siguiente paso es crear la clase la cual la hemos llamado Test1 y generar el código de la misma:

    public class Test1 {
    public int multiplica(int a, int b){
    return a * b;
    }
    public int factorial(int numero) {

    int factorial = numero;

    for(int i =(numero – 1); i > 1; i–)
    {
    factorial = factorial * i;
    }

    return factorial;
    }
    }

    Nuestra clase ahora tiene dos métodos uno llamado multiplica y otro llamado factorial.

    junit clase Test1

    El siguiente paso es crear una clase de prueba para nuestro código. Para ello hacemos click con el botón derecho del ratón y sobre el menú desplegable elegimos Tools -> Create test.

    Para la creación de test hemos elegido JUnit 4. Ten en cuenta que la estructura de los test en JUnit 3 y 4 son diferentes.

    Se creará la clase Test1Test.java, la cual tendrá un pequeño esqueleto que nos servirá en un futuro para realizar los test a nuestra clase creada.

    junit5

    /*
    * To change this license header, choose License Headers in Project Properties.
    * To change this template file, choose Tools | Templates
    * and open the template in the editor.
    */

    import org.junit.After;
    import org.junit.AfterClass;
    import org.junit.Before;
    import org.junit.BeforeClass;
    import org.junit.Test;
    import static org.junit.Assert.*;

    /**
    *
    * @author morenoperezjc
    */
    public class Test1Test {

    public Test1Test() {
    }

    @BeforeClass
    public static void setUpClass() {
    }

    @AfterClass
    public static void tearDownClass() {
    }

    @Before
    public void setUp() {
    }

    @After
    public void tearDown() {
    }

    /**
    * Test of multiplica method, of class Test1.
    */
    @Test
    public void testMultiplica() {
    System.out.println(“multiplica”);
    int a = 2;
    int b = 2;
    Test1 instance = new Test1();
    int expResult = 4;
    int result = instance.multiplica(a, b);
    assertEquals(expResult, result);

    // TODO review the generated test code and remove the default call to fail.
    //fail(“The test case is a prototype.”);
    }

    /**
    * Test of factorial method, of class Test1.
    */
    @Test
    public void testFactorial() {
    System.out.println(“factorial”);
    int numero = 3;
    Test1 instance = new Test1();
    int expResult = 6;
    int result = instance.factorial(numero);
    assertEquals(expResult, result);
    // TODO review the generated test code and remove the default call to fail.
    //fail(“The test case is a prototype.”);
    }

    }

    Para este test hemos utilizado el método assertEquals de JUnit. Esta afirmación funciona pasándole el resultado esperado y unas variables determinadas. Cuando se realiza el test, JUnit comprueba que el resultado esperado es el mismo devuelto por las variables cuando se ejecuta el método.

    Se pueden colocar tantos assertEquals como sea necesario.

    Pulsando sobre la clase a testear y eligiendo la opción test se lanza automáticamente el test creado. Dependiendo de los asserts que establezcamos, el test fallará o pasará con éxito.

    junit test

    Imaginemos que cambiamos algo el test testMultiplica de tal manera que modificamos el método como se puede ver:

    public void testMultiplica() {
    System.out.println(“multiplica”);
    int a = 2;
    int b = 2;
    Test1 instance = new Test1();
    int expResult = 5;
    int result = instance.multiplica(a, b);
    assertEquals(expResult, result);

    // TODO review the generated test code and remove the default call to fail.
    //fail(“The test case is a prototype.”);
    }

    El resultado será el siguiente:

    testfail

    Supuestamente el test del método multiplica espera un resultado de 5 pero el resultado ha sido 4. En ese caso el test automatizado ha detectado un fallo.

    Inicializadores y finalizadores en JUnit 4.

    beforeafterInializador de la clase. Es la anotación @BeforeClass. Este método de inizalización se ejecutará solamente una vez y antes de cualquiera de los otros métodos de la clase de test. Lo puedes utilizar por ejemplo para crear una conexión a una base de datos. Antes de ejecutar cualquier método se creará la conexión con la base de datos y la desconexión se haría en un método finalizador.

    Finalizador de la clase. Es la anotación @AfterClass. Indica el método finalizador de la clase. Solamente se ejecutará una vez justo después de la ejecución de los métodos de la clase test. Como se ha explicado en el inicializador, si necesitamos desconectarnos de una base de datos, este el el lugar apropiado para programarlo.

    Inicializador del test. Es la anotación @Before y marca el método que se va a ejecutar antes de cada test. No es un método necesario. Se ejecutará antes de cada test y si por ejemplo quieres inicializar variables este método sería el adecuado para hacerlo.

    Finalizador del test. Es la anotación @After y marcaría el método que se va a ejecutar después de cada test. No es un método necesario. Se ejecutará después de cada test y por ejemplo lo puedes utilizar para limpiar variables o estructuras de datos utilizadas en los test.

    Dimas quiere realizar una clase que convierta grados Farenheit a Celsius y viceversa. Conoce la fórmula y quiere implementar dos método en la clase farenheittocelsius() y celsiustofarenheit() que conviertan grados de una unidad a otra y viceversa.

    Además quiere testear que convierten los métodos correctamente los valores -5, 0, 15 y 32.

    ¿Puedes ayudarles a crear la clase y el test con JUnit 4?

    Ahora vamos a realizar un test de rendimiento de nuestro método factorial e incluimos un test para que el factorial de 300 tarde menos de un segundo. El test es el siguiente:

    @Test(timeout=1000)
    public void testFactorialTimeout() {
    System.out.println(“factorial con timeout”);
    int numero = 300;
    Test1 instance = new Test1();
    int result = instance.factorial(numero);
    }

    Lo ejecutamos y vemos el código supera satisfactoriamente el test puesto que no supera la ejecución un segundo:

    factorial timeout

    Deshabilitar test.

    En ocasiones se necesita deshabilitar uno o ciertos test. Basta con colocar @ignore en una línea antes del @test que se va a realizar para que JUnit ignore la realización del mismo. Un ejemplo sería el siguiente:

    junit test ignore

    Trabajando con excepciones..

    En ocasiones se necesita contemplar si un determinado test genera una excepción. En nuestro caso el método factorial no contempla que se le pase un número como parámetro menor que cero.

    Una llamada al factorial de un número negativo debería lanzar una excepción. A continuación reescribimos el método para contemplar este hecho:

    public int factorial(int numero) {

    int factorial = numero;
    if (numero < 0){
    throw new IllegalArgumentException("Factorial negativo!");
    }
    for(int i =(numero - 1); i > 1; i–)
    {
    factorial = factorial * i;
    }

    return factorial;
    }

    Para que nuestro test no presente ningún fallo a la hora de realizase tendremos en cuenta que el método factorial puede generar una excepción del tipo IllegalArgumentException. Para ello colocaremos (expected=IllegalArgumentException.class) después de @Test. El código quedará como sigue:

    @Test(expected=IllegalArgumentException.class)
    public void testFactorial() {

    Crear un conjunto de test o “suite de test”.

    Muchas veces necesitamos generar una serie de test para varias clases. Testear las clases por separado puede ser una tarea tediosa y más si necesitamos testear cada una de las clases en un orden determinado. La solución es crear conjuntos de test para correr varios test de una manera determinada y en unas condiciones concretas.

    Una suite será una clase con un método que invoque a los test individuales o incluso puede invocar a otras suites de test. Aunque una suite puede ser parte de una clase test, la recomendación es crear una clase aparte.

    Se pueden crear suites de test de forma manual pero la mejor opción es hacer que NetBeans la cree por nosotros. Una vez creada se podrá modificar para especificar la batería de pruebas que se crea necesaria.

    Lo primero que tenemos que hacer es crear un nuevo proyecto. Elegimos otros y escogemos la opción Unit Test y Test Suite.

    new test suite

    El siguiente paso es elegir el nombre de la suite que vamos a crear:

    NewTestSuite2

    El resultado de crear la suite de pruebas será la siguiente clase:

    /*
    * To change this license header, choose License Headers in Project Properties.
    * To change this template file, choose Tools | Templates
    * and open the template in the editor.
    */

    import org.junit.After;
    import org.junit.AfterClass;
    import org.junit.Before;
    import org.junit.BeforeClass;
    import org.junit.runner.RunWith;
    import org.junit.runners.Suite;

    /**
    *
    * @author morenoperezjc
    */
    @RunWith(Suite.class)
    @Suite.SuiteClasses({Test1Test.class})
    public class NewTestSuite {

    @BeforeClass
    public static void setUpClass() throws Exception {
    }

    @AfterClass
    public static void tearDownClass() throws Exception {
    }

    @Before
    public void setUp() throws Exception {
    }

    @After
    public void tearDown() throws Exception {
    }

    }

    Observa que en @Suite.SuiteClasses se incorporarán todos los test de pruebas que queramos y en el orden que necesitemos. Basta con separarlos con comas como por ejemplo: value={clase1Test.class, clase2Test.class}

    Para ejecutar la suite hacemos click derecho sobre la clase creada (la de la suite) y elegimos Test File. Automáticamente se ejecutará la suite con los test que incluyamos.

    ejecución suite

    Dimas se ha animado y dentro del mismo proyecto quiere realizar una clase que convierta números romanos a decimal y viceversa (métodos roman2dec y dec2roman). Quiere realizar un par de test con JUnit para que testee los números XXI y 2016.

    Además cree que puede ser útil una segunda clase que convierta dólares a euros y viceversa (métodos dollar2euro y euro2dollar), también con sus test en JUnit que testeen 10,5 dólares y 20,30 euros.

    Por último, para no realizar los test por separado lo que se propone es crear una suite de pruebas que testee las tres clases.


    5 Planificación de Pruebas.


    La planificación de las pruebas es un punto importante en la toma de decisiones de un proyecto. Qué tipo de pruebas y cuándo se van a realizar son preguntas que hay que tener en cuenta desde el principio.

    Si los proyectos son grandes (tienen envergadura) lo más normal es que se realicen las pruebas que se comentarán a continuación. En caso contrario (proyectos pequeños con bajo presupuesto), serán los responsables técnicos del desarrollo los que tendrán que establecer la estrategia adecuada.

    Dimas le pregunta a Andrino, el responsable de la empresa MyFPsoft. “¿Por qué no hacemos las pruebas de nuestro proyecto al final?”
    Andrino se quedó un poco sorprendido y le dijo: “me gusta que me hagas esa pregunta.

    Durante mi etapa de programador, estuve trabajando en una empresa pequeña con proyectos no muy grandes y como las pruebas eran aburridas y creiamos que éramos los mejores porque yo estaba seguro que mis programas funcionaban siempre, las pruebas las hacíamos al final.

    El problema que me ocurrió en mi primer proyecto fué que fallaba por todas partes y muchas veces no era capaz de diagnosticar la causa de los fallos. Me tiraba horas pensando en qué podría haber fallado el programa pero como lo había codificado hace mucho tiempo y la documentación que hacíamos era mala, los fallos eran difíciles de arreglar, y los arreglos no eran de gran calidad.

    Ahora, como soy responsable, me gusta trabajar correctamente y vamos a seguir el plan de pruebas establecido a rajatabla. Si no te parece bien te puedo poner en contacto con otras empresas por si te interesa trabajar en alguna de ellas”.

    Dimas se quedó sorprendido por la respuesta, “no, solamente era una pregunta. Me gusta mucho trabajar aquí”.

    La verdad es que la respuesta de Andrino fué un poco tosca pero en el fondo tiene mucha razón. “Tiene que ser importante seguir el plan de pruebas cuando me ha contestado así”.

    A continuación se resumen las pruebas y momento en el que se realizan las mismas.

    Pruebas unitarias.

    Se suelen realizar durante las primeras fases de diseño y desarrollo. Obviamente no hay que demorar mucho la realización de dichas pruebas puesto que luego hay que integrar todo el software (las distintas unidades) y los fallos se van acumulando y la localización y diagnóstico se complican.

    En el caso de la POO, las pruebas unitarias se deberían realizar a nivel de objeto y luego a nivel de paquete o librería. No tiene sentido probar un paquete por separado si no se han realizado pruebas de los objetos a nivel individual.

    Pruebas de integración.

    Una vez que los componentes individuales se han probado es momento de ir integrando módulos. Las pruebas de integración tendrán que hacerse al final de la fase de diseño (se realizarán pruebas para corroborar que el diseño es factible y eficiente) y también al final de la fase de codificación (una vez realizadas todas las pruebas individuales se integran componentes y se prueban en conjunto verificando que funcionan correctamente de forma conjunta).

    Existen pruebas de integración ascendentes y descendentes. Podemos probar los módulos más generales y luego ir a los más específicos o al contrario.

    pruebas integración

    Como se puede ver en la figura anterior, la filosofía de las pruebas de integración ascendentes son contrarias a las descendentes. En las descendentes generalmente hay que hacer módulos de pruebas o programas de pruebas para probar unitariamente los módulos individuales mientras que en las descendentes muchas veces hay que crear módulos, objetos y clases ficticias para probar partes más generales del programa. Aunque son filosofías diferentes el objetivo es siempre el mismo, probar que el sistema en conjunto funciona correctamente.

    Pruebas de aceptación o validación.

    Este tipo de pruebas tratan de probar el sistema completo. Además de probar que los requisitos del programa se cumplen uno po uno, el equipo de pruebas mirará también si técnicamente el programa es estable y no tiene ningún fallo.

    Además habrá que probar el rendimiento del sistema modificando la carga y observando su evolución. También se harán pruebas de estrés para cerciorarse que el sistema va a responder eficientemente ante cualquier eventualidad.

    Existen en este estadío pruebas alfa las cuales se realizan en un entorno controlado y bajo unas especificaciones concretas y pruebas beta en las que los usuarios prueban el sistema en un entorno no controlado por los desarrolladores.

    Automatización de pruebas.

    Muchas veces es necesario automatizar las pruebas o repetir las mismas pruebas tras realizar mantenimientos, modificaciones o correcciones del software.

    Es siempre bueno conservar los datos, set de pruebas, programas y módulos de prueba puesto que no se sabe si van a ser necesarios en un futuro.

    En el caso que se hayan utilizado herramientas u otro sistema perfecto. Se documentará y se almacenarán en un repositorio para su ejecución automática posterior.

    6 Calidad del software.

    La calidad es un tema que desde hace años tiene una importancia en el mundo de la comercialización de productos. El mercado actual es muy competitivo y la calidad es uno de los aspectos diferenciales que hace que un producto triunfe o fracase. Basta citar a Blackberry, empresa que en medio de la vorágine de un mercado tan competitivo como el de la telefonía móvil cometió varios errores tanto estratégicos como técnicos que la relegó a un plano poco significativo.

    El software, dadas sus características, garantizar la calidad del mismo es un proceso mucho más difícil que el de otro producto dado que un proceso industrial es más fácil de testear que el proceso de desarrollo de software.

    El software tiene que estar libre de defectos y de errores y también tiene que adecuarse a los parámetros con los cuales se ha diseñado y desarrollado. Las aplicaciones informáticas están presentes en multitud de ámbitos. Tenemos tanto software empotrado como aplicaciones en dispositivos que no nos podíamos imaginar (lavadoras, televisiones, aires acondicionados, etc.).

    En los años noventa se vivió una crisis del software. Fueron en esos años en los que la calidad y el proceso de desarrollo no tenía mucha importancia en los que se vivieron las consecuencias de desarrollar un software con poca profesionalidad en muchos casos. Algunas características de esta crisis fueron:

  • Calidad insuficiente del producto final. Muchos de los errores tenían su base en un análisis pobre con una poca comunicación con el cliente.
  • Estimaciones de duración de proyectos y asignación de recursos inexactas, con el problema que ello conlleva.
  • Escasez de personal cualificado en un mercado laboral de alta demanda. Algunos programas no estaban desarrollados bajo el paradigma de la programación estructurada. No tenían una estructura racional ni lógica con lo cual los errores se multiplicaban y el mantenimiento era un suplicio.
  • Tendencia al crecimiento del volumen y complejidad de los productos. En algunos casos dichos desarrollos complejos estaban poco probados, pobremente documentados, etc.
  • Con el tiempo se ha constatado que la calidad no se mide solamente por unos parámetros de funcionamiento sino que hay otros aspectos que son importantes como el soporte, es decir, el respaldo organizacional que tiene un producto como son la formación, asistencia a problemas inesperados y el mantenimiento permanente y efectivo.

    Para evaluar dicha Calidad se lleva a cabo la evaluación y rendimiento de las aplicaciones.
    Las mediciones de rendimiento de un software pueden estar orientadas hacia el usuario (ej. tiempos de respuesta) u orientadas hacia el sistema (ej. uso de la CPU). Son medidas típicas del rendimiento diferentes variables de tiempo (tiempo de retorno, tiempo de respuesta, tiempo de reacción), la capacidad de ejecución, la carga de trabajo, la utilización, etc.

    Para evaluar el software es necesario contar con criterios adecuados que nos permitan analizar el software desde diferentes puntos de vista.

    HardInfo

    ¿Qué es una prueba de rendimiento?

    Se realizan este tipo de pruebas para comprobar cómo el software realiza una tarea determinada en un sistema con unas condiciones de trabajo concretas. Supuestamente con una carga de trabajo normal (habitual).

    Estas pruebas de rendimiento nos pueden servir para evaluar otros parámetros como el uso de los recursos, la fiabilidad del software, la escalabilidad del sistema, etc.

    Las pruebas de carga se realizan sobre el sistema simulando una serie de peticiones esperadas o un número de usuarios esperado trabajando de forma concurrente, realizando un número de transacciones determinado. En estas pruebas se evalúan los tiempos de respuesta de las transacciones. Generalmente se realizan varios tipos de carga (baja, media y alta) para evaluar el impacto y poder graficar el rendimiento del sistema.

    Otro tipo de pruebas bastante útiles son las pruebas de estrés en las que la carga se va elevando más y más para ver cómo de sólida es la aplicación y como se maneja ante un número de usuarios y transacciones extremos.

    También existen otros tipos de pruebas como las pruebas de estabilidad donde se somete de forma continuada al sistema a una carga determinada o bien pruebas de picos donde el volumen de carga va cambiando.

    Los benchmark.

    Un benchmark es una aplicación o conjunto de aplicaciones cuya finalidad es evaluar el rendimiento de un sistema.

    Existen cuatro categorías generales de pruebas de comparación:

      - Pruebas de aplicaciones-base que se encargan de ejecutar y cronometrar los tiempos de las mismas.
      - Pruebas playback que usan llamadas al sistema durante actividades específicas de una aplicación como uso
      de disco o llamadas a rutinas de gráficos, ejecutándolas aisladamente.
      - Pruebas sintéticas que enlazan actividades de la aplicación en subsistemas específicos.
      - Pruebas de inspección que no intentan imitar la actividad sino que las ejecuta directamente en su entorno productivo.

    Existen múltiples programas para llevar a cabo este tipo de pruebas como Hardinfo, Winstone o Winbench de ZDNet, etc.

    Emma les ha dicho a sus programadores (Manuela y Dimas) que tienen que hacer pruebas de rendimiento del sistema antes y durante la ejecución de las aplicaciones que desarrollan. No quiere que los clientes se quejen injustificadamente del ralentizamiento del sistema cuando dicho ralentizamiento se debe a otros motivos ajenos a las aplicaciones que MyFPSoft desarrolla.

    También le gusta tener bien atados los condicionantes de rendimiento de las aplicaciones puesto que así se puede prever cómo va a funcionar el sistema en modo real y se pueden justificar ampliaciones de servidores, máquinas, etc.

    Manuela ha investigado algunos benchmark para realizar pruebas de rendimiento como:
    CPU Zlib, CPU Fibonacci, CPU MD5 , CPU SHA1 ,CPU Blowfish o FPU Raytracing. Ayúdala a explicarle en qué consisten e inténtalas ejecutarlas en tu sistema. Para ello deberás buscar algún software que las realice.

    6.1 Medidas/métricas de calidad del software.

    Dimas ha escuchado hablar a Emma con Andrino de las revisiones técnicas formales. Como no tiene ni idea de qué son dichas revisiones se lo ha preguntado a Manuela. Ella tampoco tiene mucha idea pero dice que va a buscar en MyFPschool información sobre ellas.

    Cuando se definen los criterios de calidad (o factores de calidad) de un software al principio de un proyecto, dichos criterios se siguen teniendo en cuenta durante toda la vida del mismo.

    No puede existir ningún criterio o factor de calidad que no se pueda medir. Algunos criterios de calidad pueden ser los siguientes:

  • Número de errores por x líneas de código.
  • Número medio de revisiones realizadas a una función o módulo de programa.
  • Generalmente para evaluar los criterios de calidad se realizan RTF o revisiones técnicas formales.

    ¿Qué es una revisión técnica formal o RTF?

    El objetivo de una RTF es descubrir errores. Tanto en la lógica, como en la funcionalidad, implementación, diseño, etc.
    Se evalua el software para comprobar que sigue los estándares y protocolos establecidos.

    Estas RTF las realiza el responsable de calidad (responsable de SQA). Como es una revisión formal se cita a las partes implicadas y se informa de cómo se va a llevar a cabo y las responsabilidades.

    No se revisa todo el software sino parte del mismo.

    Una vez que se termina la reunión el responsable de SQA deberá emitir un informe con los errores o desviaciones detectados y las acciones correctivas que se tienen que llevar a cabo. Dicho responsable también tendrá que revisar que las correcciones se llevan a cabo.

    Los criterios o factores de calidad como no podía ser de otra forma se establecen mediante métricas o medidas. Veamos algunas de las métricas de calidad más utilizadas:

  • Tolerancia a errores. Mide los efectos que tiene un error sobre el software en conjunto. El objetivo es que no haya errores pero si los hay que sus efectos sean limitados.
  • Facilidad de expansión. Mide la facilidad con la que se le pueden añadir nuevas funcionalidades a un software concreto. Cuanto más fácil sea de ampliar o mejorar mejor.
  • Independencia de plataforma del hardware. Ya sabemos que un programa en Java es de los más independientes que existen. Cuanto mayor sea el número de plataformas donde se pueda ejecutar un software mejor.
  • Modularidad. Número de componentes independientes de un programa.
  • Estandarización de los datos. Se evalúa si se utilizan estructuras de datos estándar a lo largo de un programa.
  • Resumen
    Nombre del artículo
    Diseño y realización de pruebas
    Autor
    Descripción
    El siguiente artículo versa sobre el diseño y la realización de pruebas. Mostrará los diferentes tipos de prueba y cómo se realizan.

    One thought on “UD3. Diseño y realización de pruebas

    Deja un comentario

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

    Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>