Este artículo proviene de un motor de traducción automática.
Ejecución de pruebas
Pruebas de mutación súper simples
James McCaffrey
Descargar el ejemplo de código
La mayoría de los evaluadores que sé que hayan oído hablar de ensayo de mutación, pero pocos realmente hayan realizado. Prueba de mutación tiene la reputación de ser herramientas de software de terceros costosa difícil y que requiere. Sin embargo, en la columna de este mes, mostraré cómo crear una mutación de super-simple (menos de dos páginas de código y cuatro horas de tiempo) que las pruebas del sistema utilizando C# y Visual Studio. Al mantener el sistema de ensayo de mutación simple, puede obtener la mayor parte de los beneficios de un sistema de mutación de altas prestaciones a una fracción del tiempo y esfuerzo.
Prueba de mutación es una forma de medir la eficacia de un conjunto de casos de prueba. La idea es sencilla. Supongamos que comienza con 100 casos de prueba y el sistema sometido a ensayo (SUT) pasa todas las pruebas de 100. Si se altera el SUT: por ejemplo, cambiando un ">" para un "<" o cambiando un signo "+" a un "-", supuestamente introducir un error en el SUT. Ahora si vuelve a ejecutar sus 100 casos de prueba, es de esperar al menos un error de caso de prueba que indica que uno de sus casos de prueba detecta el código defectuoso. Pero, si observa que no hay errores en las pruebas, es bastante probable que el conjunto de casos de prueba ha perdido el código defectuoso y no pruebe exhaustivamente el SUT.
Es la mejor manera de ver donde yo me dirigí mirando figura 1.
Figura 1 mutación Demo ejecución de pruebas
El SUT en este ejemplo es una biblioteca denominado MathLib.dll. La técnica que presento aquí puede utilizarse para probar la mayor parte de Microsoft.Sistemas de NET Framework incluidos en archivos DLL, aplicaciones de formularios Windows Forms, ASP.NET y así sucesivamente. El sistema de mutación comienza examinando el código fuente original para el SUT, buscando el código de candidato mutar. Mi sistema sencillo sólo busca "<" y ">" operadores. Para crear y evaluar a dos mutantes, se establece el sistema de prueba. En un escenario de producción, es probable que crearía cientos o incluso miles de mutantes. El primer mutante selecciona aleatoriamente un operador para mutar, en este caso un ">" operador en carácter coloque 189 en el código fuente SUT y Muta el símbolo (token) a "<". A continuación, se genera el código fuente de DLL mutante para crear una biblioteca de MathLb.dll mutante. A continuación, el sistema de mutación llama a un conjunto de casos de prueba en el mutante SUT, registrar los resultados en un archivo. La segunda iteración se crea y comprueba a un mutante de segundo de la misma manera. El resultado del archivo de registro es:
=============
Número de errores = 0
Número de caso de prueba de errores = 0 indica el conjunto de pruebas posibles débiles (Weak)!
=============
Número de errores = 3
Esto es bueno.
=============
El primer mutante no genera errores en caso de prueba, lo que significa que debe examinar el código fuente en la posición 189 y determinar por qué ninguno de los casos de prueba ejercer ese código.
El SUT
Mi mutación sencillo probar demostración consta de tres proyectos de Visual Studio. El primer proyecto contiene el SUT y, en este caso es una biblioteca de clase de C# llamado MathLib. El segundo proyecto es un instrumento de prueba ejecutable, en este caso, una aplicación de consola de C# llamado TestMutation. El tercer proyecto se crea y genera una aplicación de consola de C# denominada mutación de los mutantes, en este caso. Para mayor comodidad, coloqué los tres proyectos en un único directorio denominado MutationTesting. Hay muchos de los archivos y carpetas para realizar un seguimiento de con las pruebas de mutación y no debe subestimar el desafío de mantenerlos organizados. Para esta demostración se ha utilizado Visual Studio 2008 pero (cualquier versión de Visual Studio funcionará) para crear una biblioteca de clase MathLib ficticio. El código fuente completo de maniquí SUT se muestra en figura 2.
Figura 2 el código fuente de todo maniquí SUT
using System;
namespace MathLib
{
public class Class1
{
public static double TriMin(double x, double y, double z)
{
if (x < y)
return x;
else if (z > y)
return y;
else
return z;
}
}
}
Observe que conserva el nombre de clase predeterminado de Class1. La clase contiene un método estático, TriMin, que devuelve el menor de tres parámetros de tipo double. Tenga en cuenta también que el SUT es deliberadamente incorrecto. Por ejemplo, si x = 2.0, y = 3.0 y z = 1.0, el método TriMin devuelve 2.0 en lugar del valor de 1.0 correcto. Sin embargo, es importante tener en cuenta que el ensayo de mutación no nomedir directamente la exactitud de la SUT; ensayos de mutación mide la eficacia de un conjunto de casos de prueba. Después de crear el SUT, el siguiente paso es guardar una copia de la línea de base del archivo de origen, Class1.cs, en el directorio raíz del sistema de ensayo de mutación. La idea es que cada mutante es una modificación única del código fuente original de la SUT y por lo que debe mantenerse una copia de la fuente original de SUT. En este ejemplo había guarda la fuente original como Class1-Original.cs en el directorio C:\MutationTesting\Mutation.
El agente de prueba
En algunas situaciones de prueba, puede que tenga un conjunto de datos de casos de prueba existentes y, en algunas situaciones dispone de un instrumento de prueba. Para este sistema de pruebas de mutación sencillo, he creado un C# console application instrumento de prueba denominado TestMutation. Después de crear el proyecto en Visual Studio, se ha agregado una referencia a la SUT: MathLib.dll que se encuentra en C:\MutationTesting\MathLib\bin\Debug. El código fuente completo para el proyecto de mazo de cables de prueba se presenta en figura 3.
Figura 3 el instrumento de prueba y datos de prueba
using System;
using System.IO;
namespace TestMutation
{
class Program
{
static void Main(string[] args)
{
string[] testCaseData = new string[]
{ "1.0, 2.0, 3.0, 1.0",
"4.0, 5.0, 6.0, 4.0",
"7.0, 8.0, 9.0, 7.0"};
int numFail = 0;
for (int i = 0; i < testCaseData.Length; ++i) {
string[] tokens = testCaseData[i].Split(',');
double x = double.Parse(tokens[0]);
double y = double.Parse(tokens[1]);
double z = double.Parse(tokens[2]);
double expected = double.Parse(tokens[3]);
double actual = MathLib.Class1.TriMin(x, y, z);
if (actual != expected) ++numFail;
}
FileStream ofs = new FileStream("..
\\..
\\logFile.txt",
FileMode.Append);
StreamWriter sw = new StreamWriter(ofs);
sw.WriteLine("=============");
sw.WriteLine("Number failures = " + numFail);
if (numFail == 0)
sw.WriteLine(
"Number test case failures = " +
"0 indicates possible weak test suite!");
else if (numFail > 0)
sw.WriteLine("This is good.");
sw.Close(); ofs.Close();
}
}
}
Observe que el instrumento de prueba tiene tres casos de prueba codificado. En un entorno de producción, probablemente tendrá varios cientos de casos de prueba que se almacenan en un archivo de texto y se podría pasar en el nombre de archivo a la página principal como args [0]. El primer caso de prueba, "1.0, 2.0, 3.0, 1.0," representa la x, y y z de parámetros (1.0, 2.0 y 3.0), seguido por el resultado esperado (1.0) para el método TriMin de la SUT. Es obvio que el conjunto de pruebas es inadecuado: cada uno de los tres casos de prueba es básicamente equivalente y tiene el valor más pequeño que el parámetro x. Pero si examina el SUT original, verá que se pasaría de hecho los tres casos de prueba. ¿Nuestro sistema de ensayo de mutación detectará la debilidad del conjunto de pruebas?
El instrumento de prueba que se repite en cada caso de prueba, analiza los parámetros de entrada y el valor devuelto esperado, llama a la SUT con los parámetros de entrada, se recupera el valor devuelto real, se compara el rendimiento real con el rendimiento previsto para determinar un resultado correcto o incorrecto de caso de pruebay, a continuación, se acumula el número total de errores en caso de prueba. Recuerde que en las pruebas de mutación, nos interesan principalmente si hay al menos un error nuevo, en lugar de comprobar cuántos casos pasan. El instrumento de prueba, escribe el archivo de registro en la carpeta raíz del programa de llamada.
El sistema de prueba de mutación
En esta sección, le guiará a través de la mutación pruebas de una línea de programa en un momento, pero omite la mayoría de las declaraciones de WriteLine utilizadas para producir el resultado que se muestra en figura 1. He creado una aplicación de consola de C# denominada mutación en el directorio de MutationTesting de raíz. El programa comienza con:
using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Threading;
namespace Mutation
{
class Program
{
static Random ran = new Random(2);
static void Main(string[] args)
{
try
{
Console.WriteLine("\nBegin super-simple mutation testing demo\n");
...
La finalidad del objeto Random es generar una posición aleatoria de mutación. Se ha utilizado un valor de inicialización de 2, pero cualquier valor funcionará correctamente. A continuación, configuro las ubicaciones de archivos:
string originalSourceFile = "..
\\..
\\Class1-Original.cs";
string mutatedSourceFile = "..
\\..
\\..
\\MathLib\\Class1.cs";
string mutantProject = "..
\\..
\\..
\\MathLib\\MathLib.csproj";
string testProject = "..
\\..
\\..
\\TestMutation\\TestMutation.csproj";
string testExecutable =
"..
\\..
\\..
\\TestMutation\\bin\\Debug\\TestMutation.exe";
string devenv =
"C:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\Common7\\IDE\\
devenv.exe";
...
Verá cómo cada uno de estos archivos se utiliza en breve. Observe que se indica al programa devenv.exe asociado con Visual Studio 2008. En vez de codificar esta ubicación, puede hacer una copia de devenv.exe y había colocado dentro de la carpeta raíz de sistema de mutación.
El programa continúa:
List<int> positions = GetMutationPositions(originalSourceFile);
int numberMutants = 2;
...
Llamar a un Ayudante GetMutationPositions método examine en el archivo de código fuente original y almacenar las posiciones de carácter de todos los "<" y ">" caracteres en una lista y el número de mutantes de conjunto para crear y probar a dos.
El bucle de procesamiento principal es:
for (int i = 0; i < numberMutants; ++i) {
Console.WriteLine("Mutant # " + i);
int randomPosition = positions[ran.Next(0, positions.Count)];
CreateMutantSource(originalSourceFile, randomPosition, mutatedSourceFile);
try {
BuildMutant(mutantProject, devenv);
BuildTestProject(testProject, devenv);
TestMutant(testExecutable);
}
catch {
Console.WriteLine("Invalid mutant.
Aborting.");
continue;
}
}
...
Dentro del bucle, el programa busca una posición aleatoria de un carácter a la de la lista de posiciones posibles de mutación y, a continuación, llama a métodos auxiliares para generar código de fuente Class1.cs mutante, generar al mutante correspondiente MathLib.dll, volver a generar el instrumento de prueba para que utilice al nuevo mutante y a continuación, pruebe el DLL de mutante, con la esperanza de generar un error. Porque es muy probable que el código de origen mutados no sea válido, incluyo el intento de generar y probar en una instrucción try-catch, por lo que yo puedo anular las pruebas de código no generable.
El método Main envuelve como:
...
Console.WriteLine("\nMutation test run complete");
}
catch (Exception ex) {
Console.WriteLine("Fatal: " + ex.Message);
}
} // Main()
Creación de código fuente de mutante
El método auxiliar para obtener una lista de posiciones de mutación posible es:
static List<int> GetMutationPositions(string originalSourceFile)
{
StreamReader sr = File.OpenText(originalSourceFile);
int ch = 0; int pos = 0;
List<int> list = new List<int>();
while ((ch = sr.Read()) != -1) {
if ((char)ch == '>' || (char)ch == '<')
list.Add(pos);
++pos;
}
sr.Close();
return list;
}
El método coinciden con mediante el carácter de un código de origen a la vez buscan mayor-que y menor-que los operadores y agregar la posición de carácter a una colección de lista. Observe que una limitación de este sistema sencillo de mutación presentado es que sólo puede mutar tokens de carácter único como ">" o "+" y no se puede tratar como símbolos de caracteres múltiples "> =". El método auxiliar realmente mutar el código de origen SUT cotiza en figura 4.
Figura 4 El método de CreateMutantSource
static void CreateMutantSource(string originalSourceFile,
int mutatePosition, string mutatedSourceFile)
{
FileStream ifs = new FileStream(originalSourceFile, FileMode.Open);
StreamReader sr = new StreamReader(ifs);
FileStream ofs = new FileStream(mutatedSourceFile, FileMode.Create);
StreamWriter sw = new StreamWriter(ofs);
int currPos = 0;
int currChar;
while ((currChar = sr.Read()) != -1)
{
if (currPos == mutatePosition)
{
if ((char)currChar == '<') {
sw.Write('>');
}
else if ((char)currChar == '>') {
sw.Write('<');
}
else sw.Write((char)currChar);
}
else
sw.Write((char)currChar);
++currPos;
}
sw.Close(); ofs.Close();
sr.Close(); ifs.Close();
}
El método de CreateMutantSource acepta el archivo de código fuente original, que se guardó ubicación anterior, junto con una posición de carácter para mutar y el nombre y la ubicación del archivo para guardar en mutante resultante. Aquí simplemente buscar "<" y ">" caracteres, pero quizá quiera considerar otras mutaciones. En general, desea mutaciones que produzcan válido de origen, no es así, por ejemplo, cambiaría ">" en "=". No también, mutantes en más de una ubicación es una buena idea, porque sólo una de las mutaciones podría generar un error en caso de prueba nueva, que sugiera que el conjunto de pruebas es buena, cuando en realidad no esté. Algunas mutaciones no tiene ningún efecto práctico (por ejemplo, un carácter dentro de un comentario de mutantes), y algunos mutaciones producirá código no válido (por ejemplo, cambiar el ">>" operador de desplazamiento a "><").
Generar y probar el mutante
El método auxiliar de BuildMutant es:
static void BuildMutant(string mutantSolution, string devenv)
{
ProcessStartInfo psi =
new ProcessStartInfo(devenv, mutantSolution + " /rebuild");
Process p = new Process();
p.StartInfo = psi; p.Start();
while (p.HasExited == false) {
System.Threading.Thread.Sleep(400);
Console.WriteLine("Waiting for mutant build to complete .
. "
);
}
p.Close();
}
Utilizar un objeto de proceso para llamar al programa devenv.exe para volver a generar la solución de Visual Studio que alberga el código de Class1.cs cambiar origen y genera al mutante MathLib.dll. Sin argumentos, devenv.exe inicia el IDE de Visual Studio, pero cuando pasa los argumentos, devenv puede utilizarse para volver a generar proyectos o soluciones. Observe que uso un bucle de retardo, pausar cada 400 milisegundos, para dar tiempo devenv.exe para terminar de crear al mutante DLL; de lo contrario, el sistema de mutación podría intentar probar al mutante SUT antes de crearlo.
El método auxiliar para reconstruir el instrumento de prueba es:
static void BuildTestProject(string testProject, string devenv)
{
ProcessStartInfo psi =
new ProcessStartInfo(devenv, testProject + " /rebuild");
Process p = new Process();
p.StartInfo = psi; p.Start();
while (p.HasExited == false) {
System.Threading.Thread.Sleep(500);
Console.WriteLine("Waiting for test project build to complete .
. "
);
}
p.Close();
}
La idea principal es que, volviendo a generar el proyecto de prueba, el nuevo mutante SUT se utilizará cuando se ejecuta el instrumento de prueba en lugar del SUT de mutante utilizado anteriormente. Si no es válido el código fuente de mutantes, BuildTestProject se iniciará una excepción.
La última parte de la mutación sencillo sistema de pruebas es el método auxiliar para invocar el instrumento de prueba:
...
static void TestMutant(string testExecutable)
{
ProcessStartInfo psi = new ProcessStartInfo(testExecutable);
Process p = new Process(); p.StartInfo = psi;
p.Start();
while (p.HasExited == false)
System.Threading.Thread.Sleep(200);
p.Close();
}
} // class Program
} // ns Mutation
Como he mencionado anteriormente, el instrumento de prueba utiliza un nombre de archivo de registro codificado y una ubicación; se puede parametrizar que pasando información como un parámetro a TestMutant y colocarlo dentro del proceso de inicia info, donde podría aceptarse por el instrumento de prueba de TestMutation.exe.
Un mundo Real, el sistema de prueba de mutación de trabajar
Prueba de mutación es simple, en principio, pero los detalles de creación de un sistema de ensayo de mutación de altas prestaciones son un reto. Sin embargo, al mantener el sistema de mutación tan sencillo como sea posible y aprovecha Visual Studio y devenv.exe, puede crear una mutación sorprendentemente efectiva las pruebas del sistema para.NETOS SUTs. En el ejemplo que he presentado aquí, debe ser capaz de crear una mutación pruebas del sistema para sus propio SUTs. La principal limitación de las pruebas del sistema la mutación de ejemplo es que, debido a que el sistema se basa en cambios de carácter único, no puede realizar fácilmente mutaciones de caracteres múltiples operadores, como cambiar "> =" a su "<" operador de complemento. Otra limitación es que el sistema sólo le brinda la posición del carácter de la mutación, por lo que no proporcionar un método sencillo para diagnosticar a un mutante. A pesar de estas limitaciones, mi ejemplo se usa el sistema correctamente para medir la eficacia de los conjuntos de pruebas para varios sistemas de software de mediano tamaño.
Dr.James McCaffrey trabaja para voltios Information Sciences Inc., donde administra capacitación técnica para ingenieros de software de trabajo en el Microsoft Redmond, Washington, campus. Ha trabajado en varios productos de Microsoft, como Internet Explorer y MSN Search. Recuperación ante desastres. McCaffrey es el autor de ".Recetas de automatización de prueba de NET"(Apress, 2006) y puede ser contactado en jammc@microsoft.com.
Gracias a los siguientes expertos técnicos de Microsoft para el examen de este artículo: Paul Koch, Dan Liebling y Shane Williams