Dieser Artikel wurde maschinell übersetzt.

 

Test Run

Supereinfache Mutationstests

James McCaffrey

Downloaden des Codebeispiels

image: James McCaffreyDie meisten Tester, die ich kenne kenne Mutation zu testen, aber nur wenige haben Sie tatsächlich durchgeführt. Mutation Tests hat den Ruf, schwierig und erfordern teure Drittanbieter-Software-Tools. Allerdings im Artikel dieses Monats zeige ich Ihnen eine Super-simple (weniger als zwei Seiten des Codes und vier Stunden) Mutation testing System mit c# und Visual Studio erstellen. Durch das Testsystem Mutation verkomplizieren, können Sie die Vorteile eines Systems vollwertige Mutation zu einem Bruchteil der Zeit und Mühe optimal.

Testen der Mutation ist eine Möglichkeit, die Effektivität einer Reihe von Testfällen zu messen. Die Idee ist einfach. Genommen Sie an, Sie beginnen mit 100 Testfälle und Ihr System unter Test (SUT) übergibt alle 100 Tests. Wenn Sie die SUT mutieren – z. B. durch Ändern einer ">" zu einer "<" oder durch ein "+" zu ändern ein "-" – Sie vermutlich einen Bug in der SUT einführen. Jetzt Wenn Sie Ihre 100 Testfälle erneut ausführen möchten, würden Sie zumindest erwarten, dass ein Test Case-Fehler, der angibt, dass eine Ihrer Testfälle des fehlerhaften Codes abgefangen. Wenn Sie keinen Testfehler angezeigt wird, dann ist aber sehr wahrscheinlich, dass Ihre Gruppe von Test Cases des fehlerhaften Codes übersehen und nicht gründlich die SUT Übung.

Die beste Möglichkeit für Sie, um zu sehen, wo ich bin ist Spitze suchen Abbildung 1.

Mutation Testing Demo Run

Abbildung 1 Mutation Test Demo ausführen

Die SUT in diesem Beispiel wird ein Bibliothek mit dem Namen MathLib.dll. Die Technik, die ich hier vorstellen kann verwendet werden, um die meisten Microsoft zu testen.NET Framework-Systeme, einschließlich der DLLs, WinForms-Anwendungen, ASP.NET-Webanwendungen und So weiter. Das Mutation System beginnt durch Scannen von den ursprünglichen Quellcode für die SUT, Kandidatencode mutieren gesucht. Mein super-simple System sucht nur nach "<" und ">" Operatoren. Das Testsystem ist zum Erstellen und zum Auswerten von zwei Mutanten festgelegt. In einem Produktionsszenario würden Sie wahrscheinlich Hunderte oder sogar Tausende von Mutanten erstellen. Die erste Mutante wählt nach dem Zufallsprinzip einen Operator zu mutieren, in diesem Fall ein ">" Operator Zeichen 189 im Quellcode SUT positionieren und das Token mutiert "<". Als Nächstes baut mutant DLL-Quellcode um eine mutant MathLb.dll-Bibliothek zu erstellen. Das Mutation System ruft dann eine Reihe von Testfällen auf der Mutante SUT, Protokollierung von Ergebnissen in einer Datei. Die zweite Iteration erstellt und testet einen zweiten Mutant auf die gleiche Weise. Das Ergebnis der Protokolldatei lautet:

=============
Zahl der Fehler = 0
Nummerieren der Testfall-Fehler = 0 zeigt mögliche schwache test Suite!
=============
Zahl der Fehler = 3
Das ist gut.
=============

Die erste Mutante haben Testfall Fehler, generieren, d. h., Sie sollten den Quellcode an Position 189 überprüfen und bestimmen, warum Ihr Test-Fälle, dass Code Übung.

Die SUT

Meine super-simple Mutation Test Demo besteht aus drei Visual Studio-Projekte. Das erste Projekt enthält die SUT und in diesem Fall ist eine C#-Klasse Bibliothek mit dem Namen MathLib. Das zweite Projekt ist eine ausführbare Datei, in diesem Fall der C#-Konsolenanwendung mit dem Namen TestMutation Testumgebung. Das dritte Projekt und erstellt den Mutanten in diesem Fall eine c#-Konsolenanwendung mit dem Namen Mutation. Der Einfachheit platziert ich alle drei Projekte in einem einzigen Verzeichnis mit dem Namen MutationTesting. Mit Mutation Tests sind viele Dateien und Ordner zu verfolgen, und Sie sollte nicht unterschätzen, die Herausforderung der Einbrecher organisiert. In dieser Demo verwendete ich Visual Studio 2008 (jedoch Visual Studio-Version funktioniert), erstellen ein dummy MathLib-Klasse Bibliothek. Den gesamten Quellcode für das Dummy SUT wird angezeigt Abbildung 2.

Abbildung 2 den gesamten Dummy SUT-Quellcode

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;
    }
  }
}

Beachten Sie, dass ich den Standardnamen für die Klasse von Class1 beibehalten. Die Klasse enthält eine einzelne statische Methode TriMin, die die kleinste der drei Typparameter double zurückgibt. Beachten Sie außerdem, dass die SUT absichtlich falsch ist. Beispielsweise wenn X = 2.0, y = 3.0 und Z = 1.0, TriMin der Methodenrückgabe 2.0 anstelle des korrekten Wertes 1.0. However, it’s important to note that mutation testing does notdirectly measure the correctness of the SUT; mutation testing measures the effectiveness of a set of test cases. Die nächsten Schritt werden nach dem Erstellen der SUT, eine Baseline-Kopie der Quelldatei, Class1.cs, in das Stammverzeichnis des Systems testen Mutation gespeichert. Die Idee ist, dass jede Mutante eine einzelne Änderung des ursprünglichen Quellcodes von der SUT ist und daher eine Kopie der ursprünglichen Quell-SUT aufrechterhalten werden muss. In diesem Beispiel speicherte ich die ursprüngliche Quelle Class1-Original.cs im Verzeichnis C:\MutationTesting\Mutation.

Die Testumgebung

In einigen Testsituationen haben eventuell einen vorhandenen Satz von Testfalldaten- und in einigen Situationen Sie haben eine vorhandene Testumgebung zu testen. Für diese super-simple-Mutation System testen erstellt habe ich eine C#-Konsole Anwendung Testumgebung mit dem Namen TestMutation. Nach dem Erstellen des Projekts in Visual Studio einen Verweis auf das SUT hinzugefügte: MathLib.dll am C:\MutationTesting\MathLib\bin\Debug. Der gesamten Quellcode für das Testprojekt Kabelbaum wird angezeigt, Abbildung 3.

Abbildung 3 die Testumgebung und Testdaten

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();
    }
  }
}

Beachten Sie, dass die Testumgebung Testfälle für drei hartcodiert wurde. In einer Produktionsumgebung müssen Sie wahrscheinlich viele Hunderte von Testfällen, die in einer Textdatei gespeichert, und Sie konnte den Dateinamen in Main wie Args [0] übergeben. Der erste Test Case "1.0, 2.0, 3.0, 1.0," stellt die X, y und Z-Parameter (1.0, 2.0 und 3.0), gefolgt von dem erwarteten Ergebnis (1.0) für die TriMin-Methode der SUT. Es ist offensichtlich der Test ist unzureichend: jede der drei Testfälle ist im Grunde gleichwertig und hat den kleinsten Wert als X-Parameter. Aber wenn Sie die ursprüngliche SUT untersuchen, sehen Sie, dass alle drei Testfälle tatsächlich übergeben würden. Erkennt unser Testsystem Mutation der Schwäche des Test-Sets?

Die Testumgebung durchläuft jeden Testfall analysiert die Eingabeparameter und der erwartete Rückgabewert der SUT mit den Eingabeparametern aufruft, ruft den tatsächlichen Rückgabewert, vergleicht die tatsächliche Erträge aus, mit der erwartete Ertrag aus, um einen Testfall Erfolg/Misserfolg Ergebnis zu bestimmen, und dann sammelt die Gesamtzahl der Testfall-Fehler. Wie bereits erwähnt, Mutation zu testen, ob in erster Linie Sperrtasten ist mindestens eine neue Fehler vorliegt, sondern testen, wie viele Fälle übergeben. Die Testumgebung schreibt die Protokolldatei in den Stammordner des aufrufenden Programms.

Die Mutation Testing System

In diesem Abschnitt werde ich gehen Sie durch die Mutation Testen der Anwendung eine Zeile zu einem Zeitpunkt jedoch auslassen der Großteil der WriteLine-Anweisungen verwendet, um die Ausgabe in produzieren Abbildung 1. Ich erstellte eine c#-Konsolenanwendung mit dem Namen Mutation in das Root-Verzeichnis MutationTesting. Das Programm beginnt mit:

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");
...

Das Random-Objekt dient eine zufällige Mutation Position generiert. Ich habe einen Startwert von 2 verwendet, aber einen beliebigen Wert funktionieren einwandfrei. Richten Sie nun kann ich die Dateispeicherorte:

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"; 
...

Sie werden sehen, wie jede dieser Dateien in Kürze verwendet wird. Beachten Sie, dass ich auf die zugeordnete Visual Studio 2008 devenv.exe-Programm zeigen. Anstelle von hartzucodieren dieser Stelle ich konnte eine Kopie der devenv.exe vorgenommen haben und es innerhalb der Mutation Systemstammordner platziert.

Das Programm weiterhin:

List<int> positions = GetMutationPositions(originalSourceFile);
int numberMutants = 2;
...

Ich nenne Helfer GetMutationPositions Methode über die ursprünglichen Quellcodedatei Scannen und speichern die Positionen aller "<" und ">" in einer Liste und Festlegen der Anzahl von Mutanten erstellen und Testen auf zwei Zeichen.

Die wichtigsten Verarbeitungsschleife ist:

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;
  }
}
...

Innerhalb der Schleife das Programm Ruft einen zufälligen Position eines Zeichens, aus der Liste der möglichen Positionen mutieren und ruft dann die Hilfsmethoden zum Generieren von Quellcode für mutant Class1.cs, erstellen die entsprechenden Mutante MathLib.dll, die Testumgebung neu zu erstellen, so dass diese die neue Mutante verwendet und Testen Sie die DLL-Mutante, Hoffnung, ein Fehler generiert. Da es durchaus möglich ist, dass mutierter Quellcode möglicherweise ungültig, umschließe ich den Versuch, erstellen und Testen in einer Try-Catch-Anweisung, so dass ich das Testen von Code nicht erstellbar abbrechen können.

Die Main-Methode umschließt bis als:

...
Console.WriteLine("\nMutation test run complete");
  }
  catch (Exception ex) {
    Console.WriteLine("Fatal: " + ex.Message);
  }
} // Main()

Erstellen von Quellcode Mutante

Die Hilfsmethode, die eine Liste der möglichen Mutation Positionen abgerufen wird:

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;
}

Die Methode marches über den Source Code um ein Zeichen zu einem Zeitpunkt suchen größer-als und kleiner-als Operatoren und die Position des Zeichens einer Listenauflistung hinzugefügt. Beachten Sie, dass eine Einschränkung dieses super-simple Mutation-System wie beschrieben ist, dass Einzelzeichen-Token nur wie mutieren können ">" oder "+" und kann nicht bekämpft Mehrfachzeichen-Token wie "> =". Die Helper-Methode, um den Quellcode SUT tatsächlich mutieren steht in Abbildung 4.

Abbildung 4 The CreateMutantSource Method

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();
}

Die CreateMutantSource-Methode akzeptiert die ursprünglichen Quellcodedatei, die gespeichert wurde früher zusammen mit einer Zeichenposition zu mutieren Weg und den Namen und den Speicherort der resultierenden mutant Datei zu speichern. Hier ich einfach überprüfen für "<" und ">" Zeichen, aber Sie möchten möglicherweise andere Mutationen in Betracht ziehen. Im Allgemeinen soll Mutationen, die hervorruft gültige Quelle, also beispielsweise, Sie wouldn't ändern ">" auf "=". Darüber hinaus nicht in mehreren Standorten als veränderliche empfiehlt sich, nur eine die Mutationen verursacht möglicherweise einen neuen Test Case-Fehler, die vorgeschlagen wird, der Test Satz gut, ist Wenn in der Tat es möglicherweise nicht. Einige Mutationen hat keine praktischen Auswirkungen (z. B. veränderliche ein Zeichen innerhalb eines Kommentars), und einige Mutationen werden ungültigen Code zu erstellen (z. B. das Ändern der ">>" Shift-Operator, um "><").

Erstellen und Testen der Mutante

Die BuildMutant-Hilfsmethode wird:

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();
}

Ich verwende eine Prozess-Objekt zum Aufrufen des devenv.exe-Programms die Visual Studio-Projektmappe neu erstellen, die enthält des Quellcodes von Class1.cs geändert und die Mutantenhäufigkeit MathLib.dll erzeugt. Ohne Argumente, devenv.exe startet Visual Studio-IDE, aber beim Übergeben von Argumenten, Devenv kann verwendet werden, Projekte oder Projektmappen neu zu erstellen. Beachten Sie, dass ich eine Verzögerungsschleife verwendet alle 400 Millisekunden anhalten, damit devenv.exe-Zeit, schließen Sie die Erstellung der Mutante DLL hat; anderweitig darauf Mutation-System konnte die Mutante SUT zu testen, bevor Sie erstellt wurde.

Die Hilfsmethode, die Testumgebung neu zu erstellen ist:

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();
}

Die Grundidee besteht darin, dass durch erneutes Erstellen das Testprojekt, das neue Mutante, SUT verwendet wird, wenn die Testumgebung, die anstelle der zuvor verwendeten Mutante SUT ausgeführt wird. Wenn Ihre mutant Quellcode ungültig ist, wird die BuildTestProject eine Ausnahme ausgelöst.

Der letzte Teil der super-simple-Mutation Test System ist eine Hilfsmethode, die Testumgebung aufzurufen:

...
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

 

Wie bereits erwähnt, verwendet die Testumgebung einen hartcodierten Protokolldateinamen und Speicherort; Sie konnte Parametrisieren von Informationen zu TestMutant als Parameter übergeben, und platzieren es innerhalb des Prozesses Info, starten, wo es von der TestMutation.exe-Testumgebung akzeptiert werden würde.

Eine reale, Mutation Testing System arbeiten

Testen der Mutation ist im Prinzip einfach, aber die Details zum Erstellen von einem Testsystem vollwertige Mutation sind eine Herausforderung.Durch das Mutation-System so einfach wie möglich und Nutzung von Visual Studio und devenv.exe zu belassen, können Sie jedoch eine erstaunlich effektive Mutation System für Tests erstellen.NET SUTs.Das hier vorgestellten Beispiel sollten Sie erstellen eine Mutation testing System für Ihre eigenen SUTs sein.Die größte Einschränkung der Probe Mutation testing System besteht darin, dass, da das System auf Änderungen der einzelnen Zeichen basiert, einfach Mutationen Mehrfachzeichen-Operatoren, z. B. eine Änderung durchführen kann nicht "> =" um seine "<" Komplementoperator.Eine weitere Einschränkung besteht, dass das System nur Sie gibt die Position der Mutation, damit es eine einfache Möglichkeit, einen Mutant diagnostizieren bereitstellen wird.Trotz dieser Einschränkungen der wurde mein Beispielsystem erfolgreich verwendet, um die Effektivität des Test-Suites für mehrere mittelständische Softwaresysteme zu messen.

Dr.James McCaffrey  Er ist auf verschiedenen Microsoft-Produkten, einschließlich Internet Explorer und MSN Search gearbeitet. Dr. McCaffrey ist Autor von ".NET Test Automation Recipes"(Apress, 2006), und erreichen Sie unter jammc@microsoft.com.

Dank an die folgenden technischen Experten von Microsoft für die Überprüfung dieses Artikels: Paul Koch, Dan Liebling und Shane Williams