|
Alas-Haz que las pruebas unitarias sean inteligentes y totalmente automatizadasprefacio Las pruebas unitarias son un medio muy eficaz para garantizar la calidad del software, ya sea desde la perspectiva del concepto de intervención temprana en las pruebas o desde las características de las pruebas unitarias que pueden verificarse a alta velocidad sin verse afectadas por la interfaz, por lo que el desarrollo guiado por pruebas que defiende la industria, el controlador de pruebas mencionado aquí se refiere más al controlador de pruebas unitarias. Sin embargo, el equipo general de desarrollo rara vez ejecuta pruebas unitarias de forma sistemática, y las pruebas para software de aplicación son realizadas más por equipos profesionales para realizar pruebas de caja negra. La mayor dificultad de las pruebas unitarias no es que la entrada y salida no puedan determinarse, al fin y al cabo, ya se determinan en la fase de desarrollo del módulo, sino que la redacción de casos de prueba unitaria consumirá muchas horas de trabajo de desarrollador y, según estadísticas relevantes, el tiempo de los casos de prueba unitaria superará con creces el tiempo de desarrollo de la función en sí. Aquí tienes algunas de las razones más comunes por las que el desarrollo no escribe pruebas unitarias: ●Los requisitos siempre son infinitos, y aún quedan requisitos funcionales que deben cumplirse en la siguiente etapa, y no hay tiempo para llenar la unidad ●Hay demasiadas pruebas unitarias que se pueden complementar, y no hay forma de empezar, así que resisto subjetivamente. ● Las pruebas unitarias son difíciles de escribir. Por un lado, la razón puede ser que la implementación de la función funcional no es lo suficientemente razonable, y por otro, no existen (o no se desconocen) marcos útiles de pruebas unitarias ni frameworks de mock. ● Las pruebas unitarias no están incluidas en la carga de trabajo. En segundo lugar, los requisitos funcionales siguen siendo inestables y el coste de escribir pruebas unitarias no es alto. En otras palabras, si los requisitos cambian mañana, no solo se eliminará el código funcional, sino también las pruebas unitarias. Si no escribes pruebas unitarias, esta parte del esfuerzo no será en vano. De hecho, la causa raíz de los puntos anteriores es que la escritura de pruebas unitarias consume demasiado tiempo, lo que finalmente lleva a la pérdida de potencia del motor pilotado por pruebas, lo que hace que la hermosa visión del desarrollo guiado por pruebas se estanque en el escenario real, porque es demasiado difícil y costoso construir el motor para esta conducción. Las distintas unidades "x" del mercado y los marcos de pruebas unitarias solo resuelven el problema de generar marcos externos orientados a pruebas, sin ninguna lógica de casos de uso ni capacidades de generación de datos basadas en un conocimiento profundo del programa. Por ello, hace que los desarrolladores sean resistentes a diversos escenarios relacionados con el desarrollo. El lanzamiento de Wings (actualmente para C) resuelve uno de los mayores problemas para los programadores y tiene el potencial de cambiar fundamentalmente el statu quo de las pruebas unitarias, lo que aliviará efectivamente la presión de las pruebas de caja negra a nivel de sistema y las pruebas automatizadas basadas en enormes recursos humanos. Los casos de prueba de restricciones se generan automáticamente por programas, y la tecnología subyacente más crítica es la tecnología de análisis de parámetros complejos. Es decir, puede definir arbitrariamente un análisis recursivo a nivel anidado a nivel de compilador para tipos arbitrariamente complejos. Sin este avance en esta tecnología crítica, el sistema automático de generación de casos de prueba sería comercialmente incapaz o evolucionaría para producir datos de prueba conformes con muy baja eficiencia. Por ejemplo, la famosa herramienta de fuzzing American Fuzzy Lop no puede identificar el tipo de estructura requerida por el programa del usuario y necesita evolucionar el algoritmo de búsqueda basándose en la capa más externa. Las características del programa son que la entrada a nivel de interfaz y los requisitos de datos de un módulo interno están muy alejados, y los datos externos suelen transformarse capa por capa de transformación compleja para convertirse en el tipo de estructura de datos requerido por el módulo interno, por lo que la cantidad de cálculo y tiempo necesarios para explorar desde la capa externa será inimaginable. Basado en el Fuzzy Lop americano, para poder generar una sentencia SQL legítima, el módulo interno del programa debe explorarse en días, lejos de ser minutos u horas. Otra limitación es que las entradas que cada programa puede asumir son datos cuidadosamente estructurados y compilados con un gran número de reglas, y es muy poco realista y extremadamente laborioso generar estos datos mediante métodos aleatorios + exploratorios. Por lo tanto, no es factible generar casos de uso generados automáticamente a partir de la caja negra así como de la entrada más externa. Si el caso de uso impulsado se genera a partir del análisis de la estructura interna del software, es necesario tener un conocimiento profundo de la estructura de compilación del software. Un sistema viable de generación de casos de prueba debe basarse en la parte central del programa (punto de entrada de clave) como el punto de entrada más adecuado. Las entradas de estos módulos han convertido las entradas difusas en parámetros altamente estructurados. Mientras estas estructuras complejas puedan identificarse, los tipos de datos complejos puedan degradarse a tipos de datos simples paso a paso y la construcción de parámetros pueda completarse al mismo tiempo, la generación de casos de uso de conducción puede completarse automáticamente. Las pruebas basadas en módulos, que pueden clasificarse como pruebas unitarias tradicionales, son la mejor manera de encontrar y contener defectos en la fase de investigación y desarrollo. Sin embargo, debido a las limitaciones de las pruebas unitarias, es necesario desarrollar un gran número de controladores, y la promoción y aplicación en la industria están muy limitadas. Por supuesto, las pruebas unitarias también pueden ejecutarse después de integrar el sistema para evitar la construcción de programas virtuales stub. El producto Wings de Nebulas Testing, que se lanzó en el mundo por primera vez hace unos días, es un sistema inteligente y totalmente automatizado de generación de casos de pruebas unitarias, que ha estudiado y resuelto las siguientes dificultades, y ahora se comparte contigo. (1) Análisis en profundidad de los parámetros del programa Wings utiliza la tecnología subyacente del compilador para formar objetos de módulo basados en el archivo fuente de entrada según la función. El objeto contiene los parámetros de entrada de la función, el tipo de valor de retorno y otra información que puede ser utilizada por el módulo de función controlador y el módulo de casos de prueba. Cada archivo es una unidad que realiza un análisis en profundidad de cada parámetro de cada función en él, y puede lograr análisis y descomposición precisos para tipos anidados, tipos complejos, etc., explicar tipos complejos capa por capa como tipos de datos básicos y generar un archivo de descripción (PSD) de la estructura de parámetros. (2) Generación automática de módulos por accionamiento de funciones Según la información de formato del archivo PSD, todas las funciones del controlador del programa fuente bajo prueba se generan automáticamente, y el proceso de pruebas unitarias ya no depende de que los desarrolladores escriban manualmente las funciones de prueba, sino que solo necesita compilar las funciones generadas y los archivos fuente en prueba, y los resultados de la prueba pueden ejecutarse y visualizarse. El controlador de prueba genera automáticamente el programa basándose en la descripción del PSD, construye completamente de forma automática todos los parámetros y variables globales necesarias que impulsan la prueba bajo prueba, y puede generar un controlador de prueba estructurado según la jerarquía de variables complejas, lo que puede ahorrar mucho tiempo en la escritura de casos de prueba unitaria. (3) Generación y gestión automática de datos de prueba Se utiliza para generar automáticamente datos de prueba, que corresponden a la información extraída por la función de prueba y los datos se almacenan en un archivo JSON con una cierta relación lógica jerárquica. Los datos y el tipo de dato tras la descomposición y expansión corresponden entre sí. Los usuarios pueden marginar arbitrariamente estos datos según los requisitos empresariales y usar archivos JSON para mostrarlos de forma estructurada y jerárquica, lo cual es muy claro. Los datos de prueba incluyen los valores de las variables globales y los valores de los parámetros cuando se llama la función bajo prueba. Wings proporciona un método de pruebas unitarias para generar automáticamente funciones de control, que incluye principalmente los siguientes pasos: Figura 1: Flujo de compilación impulsado por pruebas unitarias 1 Extracción de la información del programa bajo pruebaLa información de estructura del programa bajo prueba incluye principalmente las variables globales y la información de función del programa, y la información de función incluye principalmente el número de parámetros, tipos de parámetros y tipos de valor de retorno de la función. Lo más importante es extraer la información de símbolos y tipos de algunos tipos complejos, y analizarlos en tipos de datos básicos capa por capa para completar la construcción de variables globales y parámetros de función. Los tipos de variables se dividen generalmente en tipos básicos, tipos de construcción, tipos puntero y tipos nulos. Wings utiliza la tecnología de compilación subyacente para manejar diferentes tipos de variables de distintas maneras. (1) Tipos básicos, como int sin signo u_int=20, Wings analizará el nombre de la variable a u_int y el tipo de dato a int sin signo. (2) Tipos de construcción, los tipos de construcción se dividen aproximadamente en arrays, estructuras, comunes y tipos de enumeración. ● Tipo de arreglo, como intarray[2][3], nombre del array es array, tipo int y longitud del array 2D, comportamiento 2, columna 3. ●Tipo de estructura, para estructuras como arrays, listas enlazadas de estructuras, etc., se dividen diferentes marcadores. (3) Tipo de puntero, por ejemplo, int **ptr = 0; , analiza el puntero como un puntero de nivel 2 de tipo int. (4) Tipo nulo, que se resuelve como NULL. (5) Los tipos de sistema, como Archivo, size_t, etc., se marcan como tipos de sistema, y serán añadidos a la plantilla y asignados por el usuario. (6) Tipo de puntero de función, analizar el tipo de valor de retorno, tipo de parámetro y número de parámetros de la función Para cada unidad de compilación del programa fuente bajo prueba, la información de la función analizada se almacena en la estructura PSD correspondiente, y se describen los siguientes ejemplos de código fuente:
En el programa anterior, anular StructTypeTest3(myy_struct mm_struct[2])La estructura PSD guardada es la siguiente:
Los significados de cada nodo en el archivo PSD son los siguientes: ●StructTypeTest3 representa el nombre de la función, parmType0 representa el tipo de parámetro y parmNum representa el número de parámetros ●mm_struct representa el símbolo del parámetro de función, baseType1 representa la clasificación de tipos (tipo de dato básico, tipo construcción, tipo puntero, tipo nulo), tipo representa tipos específicos, incluyendo int, char, short, long, double, float, bool, y estos tipos de tipos sin signo y otros tipos básicos, y hay algunos tipos especiales como: ZOA_FUN tipo representa el tipo de función, StructureOrClassType representa el tipo struct, etc., y name representa el nombre del struct, union y enum type ●i_int representa el tipo base, que es la unidad de asignación más pequeña ●array_one representa el tipo de array, RowSize representa la longitud del array, y el array puede dividirse en arrays unidimensionales, bidimensionales, etc ●point representa el tipo de puntero, el puntero se divide en puntero de primer nivel, puntero de segundo nivel, etc., y el puntero general se usa como parámetro de función como un array, por lo que para el tipo básico de puntero, se utiliza el método de array de asignación dinámica para asignar valores, y el usuario puede modificar el archivo de valores correspondiente según las necesidades. ● w representa el tipo de campo de bits, y bitfileld representa el número de dígitos ●functionPtr representa el tipo de puntero de función, que analiza el tipo de parámetro, el número de parámetros y la información de valor de retorno respectivamente ●Dem significa tipo consorcio ● dy representa el tipo enum, y valor representa el valor del tipo enum ●file representa el tipo de estructura, SystemVar representa que esta variable pertenece a la variable en el archivo de cabecera del sistema; para este tipo de variable, Wings añade variables plantilla a la biblioteca de plantillas, los usuarios pueden asignar valores especiales según necesidades específicas. Por ejemplo, el tipo de archivo se gestiona de la siguiente manera:
Los usuarios también pueden añadir sus propios métodos de asignación. Para tipos de sistema, los Wings pueden distinguirse de los tipos ordinarios definidos por el usuario, y al analizar al tipo incorporado del sistema, pueden detener el análisis recursivo hacia abajo. ●g_int representa variables globales, y globalType representa variables globales ●next representa la estructura de listas enlazadas, y NodeType representa esta estructura como una lista enlazada ●returnType representa el tipo de valor de retorno de la función. 2 Generación automática de driversEn el artículo anterior, se analiza y extrae la información estructural de variables y funciones globales, y la siguiente información se utiliza para guardar en PSD y así completar la generación global del marco de conducción del programa fuente bajo prueba. La generación se divide principalmente en los siguientes aspectos: Ø Declaración de variables globales Ø Operación de asignación de parámetros de función, según el número de parámetros de función, asignan valores a su vez Ø La asignación de variables globales se realiza secuencialmente según el número de variables globales utilizadas por el análisis Ø Llamada de la función original Algunos puntos a destacar son los siguientes: ●Durante el proceso de generación del controlador, algunas funciones especiales, como funciones principales, estáticas, etc., no se procesan temporalmente porque no pueden ser accedidas por el mundo exterior. ● Para cada archivo fuente bajo prueba, se genera un archivo controlador correspondiente. ● El control de la unidad está incluido en la Driver_main.cpp para configurar automáticamente el número de pruebas de la función mediante macros La función de control generada por el programa fuente anterior es la siguiente: ● Todas las variables se nombran antes del nombre de la variable original, añadir _ ●Al obtener los datos de prueba correspondientes, las variables se asignan sucesivamente ●Para los parámetros integrados del sistema y los parámetros especiales del usuario, el método de asignación se configura uniformemente a través del método plantilla. ●Asignar y llamar parámetros a la función bajo prueba. 3 Los datos de prueba se generan automáticamenteA continuación se muestra un conjunto de datos generados en formato PSD en la Figura 3, cada conjunto de datos se guarda en formato JSON, lo que facilita la visión de la relación jerárquica de los datos.
Para cada unidad de compilación, se genera por defecto un conjunto de archivos de datos de prueba correspondientes a todas las funciones, y la generación de valores puede modificarse según el número de configuraciones. 4 MysqlSe muestran los resultados de las pruebas del programaCómo completar la generación del framework de controladores, a continuación se presenta una explicación detallada del proceso completo de generación del programa de código abierto MySQL. A continuación se muestra el diagrama principal de la interfaz de Wings probando Mysql: Haz clic en el botón Archivo para establecer el directorio del proyecto del programa fuente bajo prueba. Una vez completadas las configuraciones, haz clic en la operación de función, que incluye principalmente análisis de parámetros, generación de controladores, generación de archivos de valor y adición de plantillas. Se generan las siguientes carpetas para el análisis: Entre ellos, el módulo de análisis de parámetros genera FunXml y GlobalXml, que almacenan la información de función y la información global de variables de cada unidad de compilación extraída respectivamente. El módulo de generación de controladores se generará Wings_Projects carpeta correspondiente, que almacena los archivos de controladores de cada unidad de compilación El módulo de generación de valor almacena los datos de prueba generados para cada unidad de compilación. La siguiente figura muestra la información sobre la estructura del archivo de controladores cargada por Mysql, y el árbol de navegación a la izquierda es el archivo de controlador generado, que contiene las funciones de cada unidad de compilación, así como los parámetros y variables globales de las funciones. Haz clic en una de las unidades de compilación para cargar el archivo de controlador correspondiente y el archivo de valor correspondiente. Lo anterior es el archivo de controladores y el archivo de valores correspondientes a la generación total de Mysql, y el archivo de controladores se describe en detalle en el siguiente código. ● Para cada unidad de compilación, la referencia de la variable global es por extern. ●La función de controlador se denomina uniformemente como el método Driver_XXX, JSON se utiliza como método para obtener datos de prueba, y los tiempos representan el número de pruebas de una sola función. ●Para cada operación de asignación de parámetros, se utiliza el formato de almacenamiento PSD analizado para asignar valores a cada estructura de capa por turno. La aplicación de Wings es muy sencilla; a continuación se muestra un índice estadístico de los datos de prueba generados usando código Mysql que puede compilarse normalmente en Visual Studio 2015; por ejemplo, todo el proceso de generación no requiere intervención manual, solo necesita formular la ruta del código fuente que debe generarse y controlarse. mysqlDatos de prueba | MysqlVersión | | CNúmero de archivos de código de idioma | | Tiempo que se tarda en analizar (PSDTiempo de generación) | | El tiempo que se tarda en impulsar la generación | | El valor se genera por el tiempo que tarda en generarse | |
Instrucciones de configuración del ordenador: | Sistema operativo | | | Inter(R) Core(TM) i7-7700cpu 3.60GHz | | | | |
A continuación se muestran los resultados obtenidos utilizando la herramienta de estadísticas del código fuente, con más de 4 millones de líneas de código de prueba unitaria válido generadas por Wings de forma completamente automática. Lo que resulta aún más interesante es que se puede ver que el coste del desarrollo manual de estos códigos puede ser de hasta 1.079 meses-hombre, y el coste puede llegar a ser de 10,79 millones.
Wings ha dado a cabo el primer paso de exploración por parte del programa para generar automáticamente el programa; la primera versión está actualmente lanzada, los desarrolladores interesados pueden descargarla directamente en la plataforma Code Cloud (https://gitee.com/teststars/wings_release), la licencia comercial ofrece un periodo de experiencia ilimitada de un mes, puedes experimentar rápidamente el poder mágico de Wings, la versión en lenguaje C de Wings soporta múltiples plataformas, como Visual Studio, VXWORKS, GCC, QT, etc. Wings está diseñado y desarrollado por el equipo de pruebas (www.teststar.cc) de Nebulas, y los desarrolladores interesados pueden ponerse en contacto con el equipo de pruebas de Nebulas a través de la plataforma interactiva de Codecloud para aportar sus ideas de diseño y comentarios sobre el uso del producto (para las excelentes sugerencias adoptadas, Nebulas puede ampliar su periodo de uso gratuito al menos tres meses). Wings tiene un gen sólido y subyacente para mejorar enormemente la calidad del software, y en el futuro, optimizará profundamente la legibilidad de los programas escritos automáticamente (más cercanos al nivel de escritura de buenos programadores) y el soporte para el lenguaje C++.
|