Erfahrungen mit Visual Studio Unit Tests
Meinen Tests zugrunde lagen diverse C#-Konsolenprogramme die ich im Rahmen meiner LINQ-Webcast-Reviews erstellt hatte, vor allem zum vierten Teil - welches übrigens lange Zeit mein meistgelesener Blog-Eintrag war wenn ich nach den Feedburner-RSS-Statistiken gehe.
Mein aktuelles Programm ergibt für sich genommen nicht allzuviel Sinn, aber es ist auch nur zum Testen gedacht:
namespace ConsoleApplication3
{
public class Program
{
static int xml1()
{
return 1;
}
static int xml2()
{
return 2;
}
static void Main(string[] args)
{
xml1();
Console.ReadKey();
xml2();
Console.ReadKey();
}
}
}
Wenn man im Quellcode einen Namespace oder eine Klasse und anschließend im Kontextmenü den Eintrag “Komponententests erstellen…” auswählt können automatisch leere Test-Stubs erzeugt werden.
Es können übrigens auch automatisch Stubs für abstrahierte Datenbank-Tabellen wie den LINQ to SQL-Klassen generiert werden, mit denen ich im zweiten Teil meiner LINQ-Webcast-Reviews experimentiert hatte - jedoch scheint mir das nur bedingt Sinn zu machen, denn es werden dabei auch Stubs für Meta-Informationen gebildet, also LINQ to SQL-interne Informationen.
Ich habe nun zum Ausprobieren nachfolgende Tests erstellt. Der Übersicht wegen habe ich den umgebenden automatisch erstellten Testcode weggekürzt, da er nicht direkt relevant ist. Um die Tests auszuführen muss man das Startprojekt ändern.
namespace TestProject2
{
/// <summary>
///Dies ist eine Testklasse für "ProgramTest" und soll
///alle ProgramTest Komponententests enthalten.
///</summary>
[TestClass()]
public class ProgramTest
{
private TestContext testContextInstance;
[… gekürzt]
/// <summary>
///Ein Test für "xml1"
///</summary>
[TestMethod()]
[DeploymentItem("ConsoleApplication3.exe")]
public void xml1Test()
{
int expected = 1;
int actual;
actual = Program_Accessor.xml1();
Assert.AreEqual(expected, actual);
}
/// <summary>
///Ein Test für "xml2"
///</summary>
[TestMethod()]
[DeploymentItem("ConsoleApplication3.exe")]
public void xml2Test()
{
int expected = 2;
int actual;
actual = Program_Accessor.xml2();
Assert.AreEqual(expected, actual);
}
}
}
Im Grunde ist die Vorgehensweise einfach: Man erstellt für jede Methode einer Klasse eine Test-Methode und ruft sie – gegebenenfalls mit bestimmten Parametern – auf und vergleicht den zurückgegebenen Wert mit dem erwarteten Wert. Ist eine Übereinstimmung vorhanden ist der Test positiv durchgelaufen. Die Vorgehensweise ist daher auch nicht anders als unter Java.
Erfahrungen mit Pex
Danach habe ich mit Microsoft Pex experimentiert. Die Umschreibung liest sich auf Microsofts-Seite folgendermaßen: “Pex finds interesting input-output values of your methods, which you can save as a small test suite with high code coverage.”. Abseits von kommerziellen Verwendungszwecken kann man das Tool für akademische Untersuchungen auch unter der Microsoft Research Download License kostenlos herunterladen.
Mit Pex lassen sich auch Test-Stubs generieren, aber der Hauptvorteil liegt darin dass automatisch relevante Eingabewerte für Methoden gefunden werden. Dazu habe ich ein neues Programm erstellt:
namespace ConsoleApplication3
{
public class PexProgram
{
public static void Puzzle(int x)
{
// What value of x solves this equation? Ask Pex to find out!
if (x * 3 + 27 == 153)
Console.WriteLine("equation solved");
if (x == 303)
Console.WriteLine("Ergebnis bei 303");
if (x == 505)
x += 505;
}
public static int test(int test)
{
if (test == 300)
return 300;
if (test == 1)
return 1;
return -1;
}
public static bool teilbar(int test2)
{
if (test2 % 2 == 0)
{
Console.WriteLine("ohne Rest teilbar");
return true;
}
else
{
Console.WriteLine("mit Rest teilbar");
return false;
}
}
}
}
Danach wählt man einfach den Namespace oder eine Klasse aus und wählt im Kontextmenü “Run Pex” aus. Meine Ergebnisse bei den drei aufgeführten Methoden waren:
Puzzle(int x):
|
x |
Details |
Output |
1 |
0 |
[TestMethod] [PexGeneratedBy(typeof(PexProgramTest))] public void Puzzle599() { this.Puzzle(0); }
|
|
2 |
505 |
[TestMethod] [PexGeneratedBy(typeof(PexProgramTest))] public void Puzzle321() { this.Puzzle(505); }
|
|
3 |
42 |
[TestMethod] [PexGeneratedBy(typeof(PexProgramTest))] public void Puzzle989() { this.Puzzle(42); }
|
--- Test equation solved
|
4 |
303 |
[TestMethod] [PexGeneratedBy(typeof(PexProgramTest))] public void Puzzle94() { this.Puzzle(303); }
|
--- Test Ergebnis bei 303
|
test(int test):
|
test |
result |
Details |
Output |
1 |
0 |
-1 |
[TestMethod] [PexGeneratedBy(typeof(PexProgramTest))] public void test407() { int i; i = this.test(0); Assert.AreEqual<int>(-1, i); }
|
|
2 |
1 |
1 |
[TestMethod] [PexGeneratedBy(typeof(PexProgramTest))] public void test718() { int i; i = this.test(1); Assert.AreEqual<int>(1, i); }
|
|
3 |
300 |
300 |
[TestMethod] [PexGeneratedBy(typeof(PexProgramTest))] public void test732() { int i; i = this.test(300); Assert.AreEqual<int>(300, i); }
|
|
teilbar(int test2):
|
test2 |
result |
Details |
Output |
1 |
0 |
true |
[TestMethod] [PexGeneratedBy(typeof(PexProgramTest))] public void teilbar553() { bool b; b = this.teilbar(0); Assert.AreEqual<bool>(true, b); }
|
--- Test ohne Rest teilbar
|
2 |
1 |
false |
[TestMethod] [PexGeneratedBy(typeof(PexProgramTest))] public void teilbar96() { bool b; b = this.teilbar(1); Assert.AreEqual<bool>(false, b); }
|
--- Test mit Rest teilbar
|
Wie man sieht werden automatisch relevante Eingabewerte für die Methoden gefunden - vor allem 42 bei der Puzzle-Methode, die übrigens der interaktiven Seite pexforfun entnommen ist.
Auf der Webseite können diverse weitere Rätsel gelöst werden – es werden Ausgabewerte bei diversen Eingabe-Parametern erwartet, und man muss den Quellcode einfügen der das Rätsel löst. Als Beispiele seien return x+1; und return x!; genannt.