Testing Unitario. Laboratorio de Testing y Aseguramiento de la Calidad del Software

Documentos relacionados
Técnicas Avanzadas de Testing Automatizado

Modulo 1 El lenguaje Java

Pruebas de unidad con JUnit

Tema 3: Herencia en C++ Programación Orientada a Objetos Curso 2008/2009 Begoña Moros Valle

Centro de Capacitación en Informática

Testing. Tipos, Planificación y Ejecución de Pruebas

LABORATORIO Nº 2 GUÍA PARA REALIZAR FORMULAS EN EXCEL

Elementos requeridos para crearlos (ejemplo: el compilador)

IAP ENTORNOS INFORMATIZADOS CON SISTEMAS DE BASES DE DATOS

Tema: Arreglos de Objetos en C++.

Tecnólogo Informático- Estructuras de Datos y Algoritmos- 2009

Programación Orientada a Objetos en JAVA

Fundamentos de Investigación de Operaciones Investigación de Operaciones 1

Tema: Sobrecarga de Operadores.

Capitulo 3. Test Driven Development

Programación I: Funciones y módulos

Técnicas Avanzadas de Testing Automatizado

UML, ejemplo sencillo sobre Modelado de un Proyecto

Introducción. Ciclo de vida de los Sistemas de Información. Diseño Conceptual

En cualquier caso, tampoco es demasiado importante el significado de la "B", si es que lo tiene, lo interesante realmente es el algoritmo.

RESUMEN DE CONCEPTOS BASICOS DE PROGRAMACION JAVA

Baires. Design - Test - Automate

Base de datos en la Enseñanza. Open Office

TEMA 7: DIAGRAMAS EN UML

INSTITUTO TECNOLOGICO de la laguna Programación Orientada a Objetos en C++

Capacitación Rational Funcional Tester

Introducción a la programación orientada a objetos

Tema 6. Reutilización de código. Programación Programación - Tema 6: Reutilización de código

Introducción al Proceso de Pruebas.

ISTP CIDET COMPUTACION E INFORMATICA ARREGLOS EN JAVA

Introducción a Visual Studio.Net

Conceptos. ELO329: Diseño y Programación Orientados a Objetos. ELO 329: Diseño y Programación Orientados a Objetos

MANUAL DE USUARIOS DEL MODULO DE EVALUACIÓN DE DESEMPEÑO SISTEMA DE ADMINISTRACIÓN DE SERVIDORES PÚBLICOS (SASP)

INSTRUCTIVO DEL COMANDO MAKE

PRÁCTICA DE LABORATORIO 3 Tipo Abstrato de Dato

Apuntes de Matemática Discreta 1. Conjuntos y Subconjuntos

ALGORITMICA Y PROGRAMACION POR OBJETOS I

Arreglos. // Incluir E/S y Librerías Standard #include <stdlib.h> #include <stdio.h>

MATERIAL 2 EXCEL 2007

LEER Y ESCRIBIR ARCHIVOS O FICHEROS EN C. FOPEN, FCLOSE, MODOS DE ACCESO READ, WRITE Y APPEND (CU00536F)

EDICIÓN Y FORMATO (II)

CASO PRÁCTICO DISTRIBUCIÓN DE COSTES

OBTENER DATOS EXTERNOS

Capítulo 6. Introducción a la POO

Diseño e implementación 15% Instalación y comisionamiento 6% Operación y mantenimiento 15%

2. Estructura de un programa en Java

GUÍA TÉCNICA PARA LA DEFINICIÓN DE COMPROMISOS DE CALIDAD Y SUS INDICADORES

Introduccion al Lenguaje C. Omar Andrés Zapata Mesa Grupo de Fenomenología de Interacciones Fundamentales, (Gfif) Universidad de Antioquia

Universidad Católica del Maule. Fundamentos de Computación Especificación de tipos de datos ESPECIFICACIÓN ALGEBRAICA DE TIPOS DE DATOS

Programación Orientada a Objetos con Java

Curso de Doctorado: Tecnologías de Objetos

11. Algunas clases estándar de Java (II)

Examen escrito de Programación 1

PROBLEMAS DE PROBABILIDADES Y ESTADÍSTICA

Práctica 2: Simón dice

Orígenes y descripción de la Automatización 'Inteligente'

Actividades para mejoras. Actividades donde se evalúa constantemente todo el proceso del proyecto para evitar errores y eficientar los procesos.

Programa Presupuestos de Sevillana de Informática.

Técnicas Avanzadas de Testing Automático

Tema 1: Fundamentos de lógica, teoría de conjuntos y estructuras algebraicas: Apéndice

LOGÍSTICA DE SALIDA: Métodos de Despacho de Mercadería. Sistema. IWMS MKR Systems

GERENCIA DE INTEGRACIÓN

Parte 1 Múltiple Opción

Operación 8 Claves para la ISO

II. Relación con Terceros

PROYECTO MIS IMÁGENES,

Actualización de versión a Bizagi 10.x

DCU Diagramas de casos de uso

Inter American Accreditation Cooperation. Grupo de prácticas de auditoría de acreditación Directriz sobre:

2.2.- Paradigmas de la POO

IV. CARGUE DE SOPORTES

MANUAL DE AYUDA HERRAMIENTA DE APROVISIONAMIENTO

Ejercicio Nº 3: Realizar aumentos en una Tabla de Sueldos

Organización de Computadoras

Tema 3. Test Driven Development

PRU. Fundamento Institucional. Objetivos. Alcance

Patrones Creacionales Builder. Patrones Creacionales Abstract Factory. Patrones Creacionales Singleton. Patrones Creacionales Prototype

Estimado usuario. Tabla de Contenidos

9. Objetos y clases Clases

Sesión 3 - Movimiento Diferencial

4. EVALUACIÓN DEL PROGRAMA DE CAPACITACIÓN

Jornada informativa Nueva ISO 9001:2008

Informática I Notas del curso

Creación de Funciones de Conducción

Escuela Politécnica Superior de Ingeniería Departamento de Ingeniería Informática

CENTRO DE APOYO TECNOLÓGICO A EMPRENDEDORES, BILIB RECETA TECNOLÓGICA REALIZACIÓN DE COPIAS DE SEGURIDAD CON GSYNC

WinHIPE: edición, compilación y ejecución de programas; y generación de animaciones web. Manual de usuario.

Software para Seguimiento de Clientes. Descripción del Producto

Índice 1 Instalación de la herramienta 2 Descripción de la herramienta 2 Arranque de la aplicación 3 Proyecto 4 Diagrama de clases 5

Proceso Unificado de Rational

CAPITULO VI ESTRATEGIAS DE OUTSOURCING

Estándares para planes de calidad de software. Escuela de Ingeniería de Sistemas y Computación Desarrollo de Software II Agosto Diciembre 2008

Metodología Orientada a Objetos Clave Maestría en Sistemas Computacionales

Beatriz Pérez. Jornada de Testing en Vivo - 1, 2, 3 probando!

INVENTARIO INTRODUCCIÓN RESUMEN DE PASOS

1.2 Qué es un Sistemas de Información Geográfica?

Catoira Fernando Fullana Pablo Rodriguez Federico [MINERIA DE LA WEB] Proyecto Final - Informe Final

Los números racionales

Mesa de Ayuda Interna

EDWIN KÄMMERER ORCASITA INGENIERO ELECTRÓNICO

Transcripción:

Testing Unitario Laboratorio de Testing y Aseguramiento de la Calidad del Software

Introducción Testing ad hoc Automatización de testing Testing unitario Unidad y Suite de test GoogleTest Fixture e independencia Test parametrizados por datos

Testing ad hoc (no sistemático) Todo programador está habituado al testing. En muchos casos, la forma en la que hacemos testing es ad hoc, es decir, no sistemática. Ej: supongamos que queremos testear la siguiente función: #include <stdio.h> int main(int argc, char **argv) { int a,b,res; } if (argc!= 3) printf("uso: max <int> <int>\n\n"); else { a = atoi(argv[1]); b = atoi(argv[2]); if (a < b) res = b; else res = a; printf("resultado: %i\n\n", res); }

Testing ad hoc (cont.) En este caso, podemos testear la aplicación directamente desde la línea de comandos: $ gcc max.c -o max $ $./max 2 3 Resultado: 3 $./max 123546 94746294 Resultado: 94746294 $./max -454-3 Resultado: -3 $./max 24 6 29 Uso: max <int> <int> $

Testing ad hoc (cont.) Ej: En caso que lo que tengamos no sea una aplicación, sino una funcionalidad interna: int max(int x, int y) { if (x < y) return y; else return x; } el testing ad hoc se vuelve más difícil: tenemos que programar un arnés para la rutina (i.e., un main que la invoque).

Dificultades del Testing ad hoc Es simple para testear aplicaciones, pero no almacena los tests para pruebas futuras. Requiere la construcción manual de arneses para la prueba de funcionalidades internas (no directamente invocables desde la interfaz de la aplicación). Requiere la inspección humana de la salida de los tests: se decide manualmente si el test pasó o no.

Automatización de testing Cuando el testing se vuelve complejo y caro como el testing es opcional, hay una tendencia a abandonarlo; o se lo intenta simplificar a través de su automatización. El costo de realizar y mantener un testing automatizado debe ser menor que el costo ahorrado por la reparación de defectos encontrados tardíamente.

Economía del Testing Esfuerzo en testing Incremento del esfuerzo Esfuerzo reducido Esfuerzo en desarrollo Esfuerzo inicial Esfuerzo ahorrado

Economía del Testing Esfuerzo en testing Incremento del esfuerzo Esfuerzo creciente Esfuerzo en desarrollo Esfuerzo inicial Esfuerzo ahorrado

Automatización de testing: objetivos Aumentar la calidad: utilizando tests como especificaciones; previniendo la reintroducción de defectos (en lugar de repararlos); localizando los defectos (ubicación y causa). Comprender el SUT: utilizando los tests como documentación. Reducir (y no introducir) riesgos: utilizando los tests como red de seguridad ; sin introducir cambios en el SUT.

Automatización de testing: objetivos Ser sencillos de ejecutar: completamente automáticos. self-checking. repetibles. Ser sencillos de escribir y mantener: simplicidad. expresividad. separación de incumbencias. Requerir mantenimiento mínimo: robustez.

Automatización de testing: problemas Muchas herramientas ofrecen automatizar test que interactuan con el SUT a través de su interfaz de usuario (metáfora record and playback) Estos test acarrean los siguientes problemas Sensibilidad al comportamiento Sensibilidad a la interfaz Sensibilidad a los datos Sensibilidad al contexto

Pruebas unitarias Testing unitario Unidad y Suite de test GoogleTest Fixture e independecia Test parametrizados por datos

Testing unitario Es una metodología para testear unidades individuales de código (SUT: Software under test), preferentemente de forma aislada de sus dependencias (DOC: Depend-on component). Las unidades pueden ser de distinta granularidad: porción de código, método, clase, librería, etc. Normalmente las pruebas son creadas por los mismos programadores durante el proceso de desarrollo. Normalmente se utiliza uno o mas frameworks para su sistematización y automatización.

Testing unitario (cont.) Normalmente las unidades son las partes mas pequeñas de un sistema: funciones o procedimientos, clases, etc. pero la granularidad es variable. Test unitario 1 ejercita alp unidad 1 Test unitario 2 ejercita Test unitario 3 ejercita unidad 2 usa Test unitario 4 ejercita componente 2 usa Test unitario 5 ejercita aplicación

Testing unitario: sistematización y automatización Una librería (framework) de apoyo al testing unitario ayuda a sistematizar y automatizar parte de las tareas manuales asociadas al testing: Define la estructura básica de un test: Inicialización Ejercitación Verificación Demolición Permite almacenar los tests como scripts. Permite definir la salida esperada como parte del script.

Testing unitario: sistematización y automatización Una librería (framework) de apoyo al testing unitario ayuda a sistematizar y automatizar parte de las tareas manuales asociadas al testing: Permite organizar tests en suites: Algunas librerías encuentran todos los test definidos y construyen la suite automáticamente. Otras requieren una construcción explícita. Las suites suelen organizarse jerárquicamente.

Testing unitario: sistematización y automatización Una librería (framework) de apoyo al testing unitario ayuda a sistematizar y automatizar parte de las tareas manuales asociadas al testing: Ofrece entornos para la ejecución de tests y suites: El test runner permite ejecutar automaticamente todos los test o subconjunto de ellos, de manera independiente. El test runner permite repetir un test con diferentes datos de entrada.

Testing unitario: sistematización y automatización Una librería (framework) de apoyo al testing unitario ayuda a sistematizar y automatizar parte de las tareas manuales asociadas al testing: Reporta información detallada sobre las pruebas: El test runner genera un reporte estadístico de la ejecución de las suites, El resultado puede incluir datos de benchmarking y cobertura.

Testing unitario con Google Test Google Test Framework es una librería de apoyo al testing unitario para C/C++ que incluye: descubrimiento automático de tests, amplio conjunto de aserciones (y aserciones definidas por el usuario), test negativos, fallas fatales y no fatales, test paramétricos, etc.

Estructura de un test arrange: se preparan los datos para alimentar al programa. #include <limits.h> #include "staticroutines.h" #include "gtest/gtest.h" TEST(MaxTest, Positive) { int a = 19; int b = 4; act: se ejecuta el programa con los datos construidos. int res = max(a,b); } EXPECT_TRUE(res == a); assert: se evalúa si los resultados obtenidos se corresponden con lo esperado.

Ejecución de tests Google Test necesita un runner explícito, que detecta automáticamente todos los test, y puede ser común a todos los test de un proyecto. se inicializa el framework int main(int argc, char **argv) { ::testing::initgoogletest(&argc, argv); } return RUN_ALL_TESTS(); se ejecutan todos los test

Demo Max

Algunas aserciones ASSERT_TRUE(<expr>): verifica que <expr> evalúe a true, parando la ejecución en caso de falla. ASSERT_FALSE(<expr>): verifica que <expr> evalúe a false, parando la ejecución en caso de falla. EXPECT_TRUE(<expr>): verifica que <expr> evalúe a true. EXPECT_FALSE(<expr>): verifica que <expr> evalúe a false. ASSERT_EQ(<expected>, <actual>): verifica que <actual> y <expected> evalúen al mismo valor. Sobre punteros verifica que sean la misma dirección y no que tengan el mismo valor! NE, LT, LE, GT, GE, para verificar desigualdad, menor, menor igual, mayor, y mayor igual respectivamente.

Algunas aserciones (cont.) ASSERT_STREQ(<expected>, <actual>): verifica que los strings <actual> y <expected> tengan el mismo contenido. STRNE: verifica que tengan diferente contenido. ASSERT_STRCASEEQ(<expected>, <actual>): verifica que los strings <actual> y <expected> tengan el mismo contenido, ignorando mayúsculas/minúsculas. STRCASENE: verifica que tengan diferente contenido ignorando mayúsculas/minúsculas. ASSERT_PREDn(<pred>, <val1>,..., <valn>): verifica que la función <pred> (de n parámetros) aplicada a <val1>,...,<valn> devuelva true.

Algunas aserciones (cont. 2) ASSERT_FLOAT_EQ(<expected>, <actual>): verifica los valores float <expected> y <actual> sean casi iguales. DOUBLE: para doubles ASSERT_NEAR(<val1>, <val2>, <error>): verifica que la diferencia entre <val1> y <val2> no supere el valor <error>. ASSERT_THROW(<prog>, <exception>): verifica que el programa <prog> lance la expeción <exception>. ASSERT_ANY_THROW(<prog>,): verifica que el programa <prog> lance cualquier excepción. ASSERT_NO_THROW(<prog>,): verifica que el programa <prog> no lance ninguna excepción. También tienen sus versiones EXPECT

Test negativos en Google Test Los tests negativos son aquellos en los que probamos que el SUT falla al ejecutar cierta porción de código bajo ciertas entradas. Con Google Test, podemos capturar los tests negativos con aserciones especiales: TEST(DeathTest, SegmentationFault) { ASSERT_DEATH({ int *n = NULL; int x = *n;}, "Segmentation fault:.*"); } TEST(DeathTest, NormalExit) { EXPECT_EXIT(NormalExit(), ::testing::exitedwithcode(0), "Success"); } TEST(DeathTest, SigKill) { EXPECT_EXIT(KillMyself(), ::testing::killedbysignal(sigkill), "Sending..."); }

Ejercicio Utilizando la clase Point: 1.Implementá el método equals. 2.Construí una suite de test para la clase, incluyendo al menos un test por método. 3. Descubriste algún bug? Corregilo.

Suites de test Cuando el SUT está compuesto por varias clases, o simplemente varios métodos de una clase, resulta claro que debemos organizar los tests. Los tests se organizan en test suites: una test suite es simplemente un conjunto de tests. Con Google Test, varios test lógicamente relacionados se puede agrupar en bajo un mismo nombre de Test Case. Una Suite (Fixture) es un conjunto de test bajo un mismo nombre de Test Case, definidos en una misma clase.

Suites de test y datos compartidos En muchos casos, los tests de una suite comparten los datos que manipulan. En estos contextos, suele ser útil organizar los tests definiendo procesos comunes de inicialización (o arrangement, o setup) para todos los tests. Similarmente, se pueden definir procesos comunes de destrucción (o teardown), que se ejecuten luego de cada test.

Suites de test y datos compartidos: un ejemplo Consideremos un ejemplo de testing de una clase Minefield, que representa el estado de un campo minado en el juego Buscaminas : class Mine { #include bool mined; "Mine.h" bool marked; class bool Minefield opened; { public: Mine field[8][8];! Mine(); public: void void setmined(bool open(int x, ismined); int y); bool void ismined(); close(int x, int y); void void setmarked(bool mark(int x, ismarked); int y); bool void ismarked(); unmark(int x, int y); void void setopened(bool putmine(int isopened); x, int y); bool void isopened(); removemine(int x, int y);! int minedneighbours(int x, int y);! }; }; Para poder testear minedneighbours, debemos crear el minefield, y ubicar minas en lugares específicos.

Suites de test y datos compartido: x un ejemplo x El proceso setup xen el siguiente ejemplo crea el escenario x x adecuado x para la ejecución de tests de minedneighbours. Se ejecuta antes de cada xtest. #include "Minefield.h" #include "gtest/gtest.h" class MinefieldTest : public ::testing::test {! protected:! Minefield testingfield;!! void SetUp() {!! testingfield.putmine(0, 0);!! testingfield.putmine(3, 4);!! testingfield.putmine(4, 3);!! testingfield.putmine(2, 2); TEST_F(MinefieldTest, NoOfMinedNeighbours01) { int number = testingfield.minedneighbours(1,!! 0); testingfield.putmine(0, 7); ASSERT_EQ(1, number);!! testingfield.putmine(7, 7); };!! testingfield.putmine(5, 1);!! testingfield.putmine(4, 7); TEST_F(MinefieldTest, NoOfMinedNeighbours02) {! } }; int number = testingfield.minedneighbours(7, }; 7); ASSERT_EQ(0, number); x

Sobre setup y teardown Es importante que no haya dependencias entre tests diferentes. No hay ninguna garantía sobre el orden en el que se ejecutan los tests GoogleTest construye un fixture fresco para cada test. testingfield = new Minefield(); testingfield.setup(); Las rutinas SetUp y TearDown ayudan NoOfMinedNeighbours01; a setear el contexto entre diferentes tests. testingfield.teardown(); free(testingfield); El método SetUp se ejecuta antes de cada test de la suite. testingfield.setup(); testingfield = new Minefield(); NoOfMinedNeighbours02; El método TearDown se ejecuta después de cada test testingfield.teardown(); de la suite. free(testingfield);

Independencia entre Tests GoogleTest asegura la independencia entre test (aunque class MineTest : public ::testing::test { se debe tener cuidado! con datos persistentes). protected: static void SetUpTestCase() { mine = new Mine(); } class MineTest : public ::testing::test {! static Mine* mine; class MineTest : protected: public ::testing::test }; {! Mine *mine; protected: }; Mine* MineTest::mine = NULL; Mine mine; }; TEST_F(MineTest, TEST_F(MineTest, Marked) { Marked) { mine = new Mine(); mine->setmarked(true); TEST_F(MineTest, mine->setmarked(true); Marked) { ASSERT_TRUE(mine->isMarked() &&!mine->isopened()); mine.setmarked(true); ASSERT_TRUE(mine->isMarked() }; &&!mine->isopened()); ASSERT_TRUE(mine.isMarked() }; &&!mine.isopened()); }; TEST_F(MineTest, Opened) { TEST_F(MineTest, Opened) mine->setopened(true); { TEST_F(MineTest, mine->setopened(true); Opened) { ASSERT_TRUE(!mine->isMarked() && mine->isopened()); mine.setopened(true); ASSERT_TRUE(!mine->isMarked() }; && mine->isopened()); ASSERT_TRUE(!mine.isMarked() }; && mine.isopened()); };

Demo Minefield

Ejercicio Utilizando las clases Minefield y Minefield_Test: 1.Agregá a Minefield_Test más casos de prueba para testear todos los métodos de Minefield. 2.Implementá el método minedneighbours en la clase Minefield y extendé el test correspondiente.

Tests parametrizados por los datos que manipulan Ya hemos visto varios casos en los cuales varios tests poseen exactamente la misma estructura, pero difieren en los datos que se utilizan para realizar el arrange del test. Para estos casos, Google Test ofrece una forma conveniente de organizar los tests, separando su estructura de los datos que manipulan. La clave es: definir un test como paramétrico. definir un generador de parámetros para la suite, que se usará para instanciar los datos para los tests.

Test parametrizados: un ejemplo Consideremos nuevamente el método max. El siguiente test parametrizado lo prueba con varios datos diferentes: using namespace std; using ::testing::testwithparam; using ::testing::values; Indica que la suite está formada por tests parametrizados. typedef ::tr1::tuple<int, int> mytuple; class MaxTest : public TestWithParam< mytuple > { }; TEST_P(MaxTest, Test) { mytuple row; int maxval, minval; } row = GetParam(); maxval = ::tr1::get<0>(row); minval = ::tr1::get<1>(row); ASSERT_EQ(maxVal, max(minval,maxval)); INSTANTIATE_TEST_CASE_P(MaxOnLeft, MaxTest, Values( mytuple (2,1), mytuple (45,-2), mytuple (-12,-90) )); Test paramétrico (TEST_P) Datos de los test

Test parametrizados Generador Descripción El runner genera una el conjunto instancia de valores de cada en el rango test por comenzando cada dato: en Range(begin, MaxOnLeft/MaxTest.Test/0 end, step) begin, terminando para (2,1) en end (no incluido) de a step incrementos. MaxOnLeft/MaxTest.Test/1 para (42,2) Values(v1,v2,...,vn) MaxOnLeft/MaxTest.Test/2 el conjunto para de valores (-12,-90) {v1, v2,..., vn}. Puede además haber más de una generador de datos (entonces ValuesIn(container) se genera el conjunto una instancia de valores por del contenedor cada test, container. por cada dato de cada generador). GoogleTest Bool define una el conjunto serie de de valores funciones {true, false}. para definir generadores de datos: Combine(g1,g2,...,gn) el producto cartesiano (tuple< >) de g1, g2,..., gn.

Demo Max Parametrizado Largest Parametrizado

Testing unitario: beneficios El testing unitario sistematizado: Facilita los cambios: los test permiten comprobar que la unidad continúa funcionando correctamente a pesar de cualquier tipo de cambio (refactoring o nuevas funcionalidades); su sistematización facilita el testing de regresión. Simplifica la integración: los test reducen la incertidumbre sobre las componentes individuales, facilitando la integración y su verificación, ya sea bottom-up o top-down (con la integración de mocks)

Testing unitario: beneficios El testing unitario sistematizado: Sirve como documentación: los test especifican las partes criticas del comportamiento de la unidad, ya sea su uso apropiado o inapropiado; sirven como ejemplo y descripción siempre actualizada de la API. Contribuye al diseño: los test sirven como ejemplo de uso y dan feedback temprano; fuerzan a enforcarse en el qué y en el cómo, encontrando defectos de diseño y aportando a su replanteamiento.

Testing unitario: principios Primero escribir los test. Diseñar pensando en la testeabilidad. Utilizar la puerta principal. Documentar los test fixtures. No modificar el SUT. Mantener la independencia de los test. Aislar el SUT. Minimizar el solapamiento. Minimizar el código inestable. Mantener la lógica de testing fuera del código de producción. Verificar sólo una condición por test. Probar diferentes incumbencias de forma separada.

Ejercicio Considerando los métodos definidos en staticroutines: 1.Extendé el test de largest para incluir más casos. 2.Construí una suite paramétrica para testear bubblesort. 3.Si encontrás bugs, corregilos.

Referencias Google Test: code.google.com/p/googletest/