NUnit - Testgetriebene Entwicklung unter .NET

Transcription

NUnit - Testgetriebene Entwicklung unter .NET
NUnit - Testgetriebene Entwicklung unter .NET
Martin Blersch
Dr.-Ing. Marc Schanne
Forschungszentrum Informatik
Universität Karlsruhe
Email: [email protected]
Forschungszentrum Informatik
Universität Karlsruhe
Email: [email protected]
Zusammenfassung—NUnit ist ein Rahmenwerk für die Erstellung von Komponenten- und Modultests (Unit-Tests) für die
.NET-Plattform. Es unterstützt den Entwickler bei der testgetriebenen Entwicklung und trägt zu einer Steigerung der Codequalität bei, indem Fehler im Quellcode schnell erkannt werden
können. Dies wiederum hilft Kosten zu sparen, die bei einer
nachträglichen Fehlersuche anfallen würden. Wir versuchen die
Vorteile durch den Einsatz von NUnit, sowie dessen Grenzen zu
zeigen. Die wichtigsten Behauptungen (Asserts) werden vorgestellt und anhand eines Beispiels erklärt.
I. S OFTWAREENTWICKLUNG VS . S OFTWARETESTS
Häufig wird in der Softwareentwicklung auf ausgiebige
Softwaretests aus Zeit- und Geldgründen verzichtet. Tests sind
jedoch wichtig, denn sie steigern die Qualität und die Zuverlässigkeit eines Softwareprodukts. Daher nimmt das Testen
in großen Softwareprojekten einen ähnlich hohen Stellenwert
ein wie das Programmieren selbst und verursacht mittlerweile
bis zu 50% der Kosten des kompletten Softwareentwicklungsprozesses. Durch die immer höheren Anforderungen an
die Softwarequalität wird diese Rate vermutlich noch einmal
steigen.
Softwaretests werden oft als lästig empfunden. In der Regel
ist die Zeit bei Projekten sowieso sehr knapp bemessen und
durch die Entwicklung von guten Tests verlieren die Programmierer nochmals Zeit, in der sie nicht an der Funktionalität
der Software arbeiten können. Vielmehr müssen sie Testfälle
definieren, implementieren und später auswerten.
II. WARUM ALSO T ESTS ?
Mit steigendem Fortschritt und Komplexität eines Projekts
steigen auch die Kosten für die Fehlerbeseitigung exponentiell. Das frühe Erkennen von Fehlern in Klassen, Methoden
und Komponenten durch Softwaretests zahlt sich somit aus
und die Qualität der Software nimmt zu. Tests sichern ein
Mindestmaß an Leistung und Zuverlässigkeit und sind aus der
Softwareentwicklung nicht mehr wegzudenken. Gesucht wird
eine kostengünstige Testmethode die möglichst viele Fehler
aufdeckt, leicht zu implementieren ist und die automatisiert
werden kann, um auch nach Modifikationen von bereits getesteten Teilen der Software erneut benutzt werden zu können
(Regressionstests). Dies ist gerade bei Software die häufig
geändert werden muss wichtig, jedoch auch bei der Pflege,
Weiterentwicklung oder Fehlerkorrektur eines Softwareprodukts.
Studien haben gezeigt, dass Komponenten- und Modultests
(Unit Tests) im Vergleich zu anderen Testmethoden das beste Kosten-Nutzen-Verhältnis haben. Trotz höherem initialen
Aufwand für die Komponentenerstellung erreicht man dadurch
geringere Folgekosten bei der späteren Fehlersuche und Beseitigung.
Unit Tests sind der effektivste Weg um möglichst viele Fehler im Code aufzudecken. Sie bieten eine Alternative gegenüber Debugging und Standardausgaben, die, gerade bei komplexen Softwareelementen oder bei Schleifen,
häufig zu unübersichtlichem Code führen. Bereits bei einer
Schleife mit vielen Durchläufen sind die Ausgaben kaum
noch überschaubar. Debugging ist gerade bei Programmen
mit mehreren Threads nur umständlich möglich und erfordert
vom Entwickler hohe Konzentration und Wissen über das
Gesamtsystem. Deshalb ist es sehr fehleranfällig.
Komponententests ersetzen keine Integrations-, System-,
Last- oder Performancetests, denn das korrekte Funktionieren
aller Tests bedeutet nicht, dass deren Zusammenspiel ebenfalls frei von Fehlern ist. Dies muss durch Integrationstests
überprüft werden. Sie testen das korrekte Zusammenspiel von
Komponenten in einem System, die voneinander abhängig
sind. Dazu bedient man sich einer Reihe aufeinander abgestimmter Einzeltests. Komponententests kann man auch
als Vorstufe von Integrationstests bezeichnen. Zur Sicherstellung des spezifikationsgemäßen Funktionierens des Gesamtsystems sind Tests auf Systemebene nötig. Neben funktionalen Qualitätsmerkmalen wie Korrektkeit und Vollständigkeit
überprüfen Systemtests ebenfalls nichtfunktionale Eigenschaften. Last- und Performancetests sind Softwaretests die das
Verhalten eines laufenden Systems unter Last beobachten und
bewerten.
III. KOMPONENTENTESTS MIT NU NIT
NUnit ist ein Rahmenwerk zum Schreiben und Ausführen
von automatischen Unit Tests für .NET Sprachen. Es bietet
eine grafische Benutzerschnittstelle und ist vollständig in C#
geschrieben. Anfänglich wurde das Testrahmenwerk durch
Philip A. Craig von JUnit portiert und von den Entwicklern
Jim Newkirk, Michael Two, Alexei Vorontsov und Charlie
Poole weiterentwickelt sowie an die .NET-eigenen Fähigkeiten
angepasst. NUnit ist Open Source, steht unter der zlib/libpng
Lizenz1 und darf daher auf die eigenen Bedürfnisse angepasst
1 http://www.opensource.org/licenses/zlib-license.html
und verändert werden. Die Nutzung ist zudem kostenlos. Eine
ausführliche Dokumentation sowie einen Downloadlink zur
aktuellen NUnit- Version findet man auf der Herstellerseite2 .
IV. VORGEHENSWEISE
Parallel zur Programmierung, oder bereits vor dem Implementieren der eigentlichen Klasse oder Funktion, werden passende Unit Tests entwickelt und implementiert. Dies
bietet dem Entwickler die Möglichkeit seinen geschriebenen Code umgehend überprüfen und gegebenenfalls ändern
zu können. NUnit ist ein Rahmenwerk, das Testerstellung,
sowie Zusammenfassen und Ausführen verschiedener Tests
unterstützt. Es bietet eine grafische Benutzeroberfläche (GUI),
die übersichtlich die fehlgeschlagenen und korrekten Tests
anzeigt. Die Tests werden in einer eigenen Testklasse ausgelagert und lassen den Programmcode des Softwareprodukts
unberührt. Der Entwickler kann Behauptungen (Asserts) in
die Testobjekte einbauen. Treffen diese Behauptungen nicht
zu, schlägt der Test fehl. Unit Tests sind White-Box-Tests,
das heißt eine Kenntnis über die innere Funktionsweise des
zu testenden Systems wird vorausgesetzt. Der Entwickler
kann somit testen ob alle internen Operationen der vorgegebenen Spezifikation entsprechen. NUnit unterstützt ihn bei
der Testerstellung, Testdatengewinnung, Testtreiber- und StubErstellung, Testausführung und Testauswertung durch Automatisierung.
Das Fenster ist, wie in Abbildung 1 dargestellt, in zwei
Bereiche aufgeteilt. Die linke Spalte zeigt die ausgewählte
Testklasse sowie die darin enthaltenen Tests in einer Baumstruktur. Werden die Tests ausgeführt zeigt ein grüner, gelber
oder roter Punkt vor den Tests sowie vor der Testklasse das
Ergebnis an. Eine erfolgreiche Behauptung wird durch einen
grünen Punkt symbolisiert. Nicht ausgeführte Tests erscheinen gelb, Fehlgeschlagene rot. In derselben Farbe ist in der
rechten Spalte ein Balken zu sehen, der das Ergebnis noch
einmal verdeutlicht. Darunter werden in einem Fenster die
eventuell vorhandenen Fehler aufgelistet. Es zeigt den Namen
des fehlgeschlagenen Tests sowie das erwartete Ergebnis der
Behauptung (Assertion) und das tatsächliche Ergebnis an. Dies
ermöglicht eine schnelle Fehlersuche und -beseitigung. Ein
neu zur Testklasse hinzugefügter Unit Test taucht als weiterer
Ast in der linken Spalte der grafischen Oberfläche auf.
V. T ESTGETRIEBENES E NTWICKELN MIT U NIT-T ESTS
Der Einsatz von Unit Tests ist ein wesentlicher Bestandteil in der agilen Softwareentwicklung. Dabei bemüht man
sich schnell auf Änderungswünsche des Kunden reagieren zu
können, was einen hohen Grad an Flexibilität voraussetzt. Der
Code muss ständig neu geschrieben und angepasst werden.
Automatisierte Unit Tests sollen sicherstellen, dass das Programm nach einer Änderung noch funktioniert.
Agile Prozesse wie Extreme Programming“ verlangen je”
weils mindestens einen Test pro Modul bzw. Komponente [1]. Dabei sollte keine Methode ungetestet bleiben. Dies
ermöglicht bei einem Fehler im Programm eine genaue Lokalisierung. Er kann schnell entdeckt und behoben werden.
Testfälle werden noch vor der eigentlichen Funktionalität
implementiert und zwingen so den Entwickler sich früh mit
seinem Code-Design auseinander zu setzen. Komponententests
in der testgetriebenen Entwicklung sind Grey-Box-Tests, das
heißt eine Mischung aus White-Box-Tests und Black-BoxTests (nur die Spezifikation bekannt, nicht derern Implementierung). Eine Mischung dieser beiden Arten bietet die beste
Testfallabdeckung, da so die Vorteile beider Arten kombiniert
werden kann. Eine stetige Wiederholung aller Tests (Regressionstests) führt zu einer erheblichen Qualitätssteigerung [2].
VI. S TRUKTUR DER U NIT-T ESTS
NUnit besitzt eine grafische Benutzeroberfläche und eine
Konsole, die dieselbe Funktionalität bietet. Sie geben Auskunft
über erfolgreiche oder fehlgeschlagene Tests.
2 http://www.nunit.org/
Abbildung 1.
Grafische Benutzeroberfläche von NUnit
Neben der grafischen Oberfläche bietet NUnit eine Konsolenversion. Die Konsole speichert die Ergebnisse in einer XML-Datei. Diese Datei kann wiederum mit geeigneten Programmen weiterverarbeitet werden. Man startet die
Überprüfung einer Datei durch den Befehl nunit-console <Dateiname>, wobei der Dateiname eine .dll, .csproj oder .nunit
(NUnit-Test-) Datei sein kann. Weitere Befehle sind auf der
Webseite http://www.nunit.org/ zu finden.
VII. AUFBAU EINER T ESTKLASSE
Testfälle werden über eigene Benutzerattribute (Custom
Attributes) im .NET Quellcode definiert und mittels Reflection
ausgelesen.
A. Beispielklasse
namespace NUnitTestingExamples
{
using System;
using NUnit.Framework;
[TestFixture]
public class SuccessTests
{
[SetUp] public void Init()
{ /* ... */ }
[TearDown] public void Dispose()
{ /* ... */ }
[Test] public void Test1()
{ /* ... */ }
[Test]
[ExpectedException(typeof(XYZ)]
public void Test2()
{
// Something that throws an
// XYZ Exception
}
[Ignore("skip this test")]
public void IgnoredTest()
{ /* ... */ }
}
A SSERT.NAME D ER B EHAUPTUNG ( OBJECT EXPECTED , OBJECT ACTUAL )
Gleichheitsbehauptungen
(Equality Asserts)
AreEqual, AreNotEqual
Identitätsbehauptungen
(Identity Asserts)
Are(Not)Same, Contains
Vergleichsbehauptungen
(Comparison Asserts)
Greater, Less, GreaterOrEqual,
LessOrEqual
Typ Behauptungen
(Type Asserts)
Is(Not)InstanceOfType,
Is(Not)AssignableFrom
Bedingungsbehauptungen
(Condition Tests)
IsTrue, IsFalse, Is(Not)Null,
IsNaN, Is(Not)Empty
Gebrauchsmethoden
(Utility Methods)
Fail(), Ignore()
S TRING A SSERT.NAME D ER B EHAUPTUNG ( STRING EXP, STRING ACTUAL )
String Asserts
C OLLECTIONA SSERT.NAME D ER B EHAUPTUNG ( ARG1 [, arg2 ])
}
CollectionAsserts
B. Erklärung
•
•
•
•
•
•
•
Contains, StartsWith, EndsWith,
AreEqualIgnoringCase
using NUnit.Framework bindet das NUnit Framework
ein.
[TestFixture] ist eine Kollektion von Unit Tests. Dieses
Attribut muss vor der Klassendeklaration eingefügt werden, um anzugeben, dass die Klasse Tests enthält.
[SetUp] ist ein Initialisierungsabschnitt mit Code, der vor
den eigentlichen Unit Tests ausgeführt wird.
[TearDown] ist ein Codeabschnitt, der im Anschluss an
alle Tests ausgeführt wird.
[Test] ist ein einzelner Komponententest. Dieser wird vor
einer Methode eingefügt, um anzuzeigen, dass es sich um
einen Test handelt.
[ExpectedException] enthält eine Ausnahme, welche von
der Methode zurückgeben werden kann.
[Ignore] kann vor einem Unit Test angegeben werden,
um diesen zu deaktivieren. NUnit ignoriert alle Tests mit
diesem Attribut. Eine Begründung kann mit angegeben
werden.
VIII. B EHAUPTUNGEN (A SSERTS )
Mit Hilfe von Behauptungen kann der Entwickler
überprüfen, ob ein Programm oder Programmabschnitt die
vorgegebenen Spezifikationen erfüllt. Sie werden in die zugehörige Testklasse geschrieben und sollen das Verhalten
der Methoden bei bestimmten Eingaben oder Parametern
überprüfen. Behauptungen sind nach folgendem Schema aufgebaut: Assert.NameDerBehauptung(argument1, argument2,
... ). Sie sind meist mehrfach überladen und existieren mit
unterschiedlichen Parameterlisten. Eine genaue Beschreibung
der einzelnen Befehle findet man auf der Projektseite von NUnit [3]. Eine kurze Übersicht über die verschiedenen Befehle
befindet sich in der Tabelle I.
AllItemsAreInstancesOfType,
AllItemsAreNotNull,
AllItemsAreUnique, AreEqual,
AreEquivalent, AreNotEqual,
AreNotEquivalent, Contains,
DoesNotContain, IsSubsetOf,
IsNotSubsetOf, IsEmpty,
IsNotEmpty
Tabelle I
Ü BERSICHT - B EHAUPTUNGEN (A SSERTS )
Beispiel: Assert.AreEqual(object expected,
object actual).
Neu sind bei NUnit ab Version 2.4 die Behauptungen für
Zeichenketten (StringAsserts) und Sammlungen (CollectionAsserts). Ihre Schreibweise weicht von den normalen Asserts ab.
Beispiel: StringAssert.Contains("Das Wetter
ist schön", "schön") überprüft die Zeichenkette Das
”
Wetter ist schön“ auf die Behauptung sie beinhalte (contains)
schön“.
”
IX. B EISPIEL
Als Beispiel soll die Klasse Person dienen. Ein Objekt
dieser Klasse besitzt einen Namen, einen Arbeitsbereich und
ein bestimmtes Gehalt. Ferner kann eine Person Angestellte unter bzw. Vorgesetzte über sich haben. Diese wiederum sind ebenfalls vom Typ Person und können durch die
Methoden setEmployee() und setChief() hinzugefügt werden. Der Einfachheit halber ist nur eine begrenzte Anzahl
an Untergebenen (MAX EMPLOYEES) und Vorgesetzten
(MAX CHIEFS) möglich.
namespace TestProgram
public class Person
{
private string name;
private string department;
private double salary;
private Person[] chiefs;
private Person[] employees;
const int MAX_EMPLOYEES = 10;
const int MAX_CHIEFS = 10;
public Person(string name)
{
department = "";
salary = 0;
chiefs = new Person[MAX_CHIEFS];
employees = new Person[MAX_EMPLOYEES];
}
public void setChief(Person chief)
{
if (countChiefs() < MAX_CHIEFS)
chiefs[countChiefs()] = chief;
}
public void setEmployee(Person employee)
{
if (countEmployees() < MAX_EMPLOYEES)
employees[countEmployees()] =
employee;
}
public int countEmployees()
{
// return amount of employees in list
}
public int countChiefs()
{
// return amount of chiefs in list
}
}
Die zugehörige Testdatei muss den selben Namensraum wie
die Klasse Person besitzen und das NUnit Rahmenwerk durch
die using-Direktive importieren. Die Klasse PersonTest, die
die Tests der Klasse Person enthält, muss durch das Benutzerattribut (Custom Attribute) [TestFixture] gekennzeichnet sein.
using System;
using System.Collections.Generic;
using NUnit.Framework;
namespace TestProgram
{
[TestFixture]
public class PersonTest
{
Person person;
[SetUp]
public void Init()
{
person = new Person("John");
person.Salary = 5000;
person.Department = "Accountancy";
}
}
}
Die Methode Init(), die mit dem [SetUp] Attribut gekennzeichnet ist, erstellt eine Instanz der Klasse Person und weist
ihr einen Namen, ein Gehalt sowie einen Arbeitsbereich zu.
Wegen des Attributs [SetUp] wird diese Methode vor allen
anderen Methoden der Testklasse PersonTest ausgeführt und
bietet somit den anderen Testmethoden ein konkretes Objekt
für deren Tests.
Das folgende Beispiel zeigt einen einfachen Test, gekennzeichnet durch das [Test] Attribut. Er enthält drei Behauptungen (Asserts), die für einen erfolgreichen Ablauf der Testdatei allesamt korrekt sein müssen. Die Behauptung AreEqual
erachtet zwei Zeichenketten für gleich, wenn sie denselben
Inhalt besitzen. AreSame und AreNotSame prüfen zusätzlich ob
es sich dabei um das identische bzw. nicht identische Objekt
handelt.
[Test]
public void NameToStringTest()
{
Assert.AreEqual(person.ToString(),
"John");
Person person2 = new Person("John");
Assert.AreNotSame(person, person2);
Assert.AreEqual(person.ToString(),
person2.ToString());
}
Die Klasse Person enthält zwei Funktionen getChiefs(int
pos) und getEmployees(int pos), die den Vorgesetzten bzw.
den Untergebenen der jeweiligen Person an der Indexstelle pos
zurück liefert. Sie können eine IndexOutOfRange Ausnahme
auslösen, falls versucht wird auf einen Index zuzugreifen, der
größer ist als die Werte der Konstanten MAX EMPLOYEES
und MAX CHIEFS. Die Ausnahme wird in diesem Beispiel
geworfen und nicht abgefangen.
// throws IndexOutOfRangeException
public Person getEmployee(int pos)
{
try
{
return employees[pos];
}
catch (IndexOutOfRangeException ex)
{
throw new IndexOutOfRangeException(
"An Error occured: " + ex);
}
}
Die dazugehörige Testmethode GetEmployeeExceptionTest
soll genau diesen Fall testen und erwartet sogar diese Ausnahme. Dies wird durch das zusätzliche Attribut [ExpectedException], sowie dem erwarteten Ausnahmentyp als Parameter
gekennzeichnet (typeof(IndexOutOfRangeException)).
[Test]
[ExpectedException(typeof
(IndexOutOfRangeException))]
public void GetEmployeeExceptionTest()
{
// throws IndexOutOfRangeException
Assert.AreEqual(person.getEmployee(100)
.ToString(), "Julian");
}
X. VORTEILE DURCH DEN E INSATZ VON NU NIT
•
•
•
•
Tests können beliebig oft ausgeführt und wiederholt werden (reproduzierbar).
Mit dem Einsatz eines Unit Test-Tools wird eine höhere
Codequalität in kürzerer Zeit und mit weniger Entwicklungskosten erreicht.
Automatisierbar
Reduziert die Komplexität der Software durch das Zerlegen der Software in kleinere Module und Komponenten.
XI. G RENZEN VON NU NIT
•
•
•
•
•
Es ist kein Testen von private Membern möglich, da die
TestCase Klasse, von der alle Tests in NUnit ableiten
müssen in einer separaten Testklasse liegt.
Die Tests müssen als Code in eine Datei geschrieben
werden. Nur ein Programmierer kann die Testsuite modifizieren.
Mit NUnit ist nur die Geschäftslogik testbar. Für GUIElemente existieren jedoch spezielle Rahmenwerke, unter
anderem NUnitASP [4] und NUnitForms [5]
Die Testprogramme sind unter Umständen sehr aufwendig.
Tests können nur zeigen, dass es Fehler gibt, nicht, dass
es keine gibt.
XII. B EWERTUNG
Obwohl NUnit keine Universallösung ist, bietet es dem
Entwickler für die gängigsten Testfälle eine komfortable Suite.
Sie unterstützt ihn gerade bei der testgetriebenen Entwicklung
durch das einfache Erstellen von Testfällen in einer separaten
Datei. Durch die Test-First Strategie, also das Schreiben des
Tests vor der eigentlichen Implementierung der Komponente,
ist der Entwickler gezwungen, sich frühzeitig mit dem Design
des Codes auseinander zu setzen. Dies erhöht die Codequalität. Allerdings zeigen Unit Tests nur vorhandene Fehler
auf, nicht aber deren Nichtvorhandensein. Sie bieten eine
sinnvolle Erweiterung, nicht aber einen vollständigen Ersatz
anderer Testmethoden wie Integrations-, System-, Last- oder
Performancetests. Die durch die Testfallerstellung anfallenden
Kosten sind meist günstiger als eine spätere Fehlersuche.
A. Erweiterungsmöglichkeiten von NUnit
NUnit kann durch Pakete erweitert werden, um auch Elemente einer grafischen Benutzeroberfläche testen zu können.
Sie setzen allesamt auf NUnit auf und sind ebenfalls Open
Source.
• Für ASP.NET-Seiten steht NUnitASP [4] zur Verfügung.
Das Rahmenwerk unterstützt die Abfrage von Webcontrols, unter Anderem von Buttons, Labels und TextBoxen.
• Für Windows Forms Elemente steht die Erweiterung
NUnitForms [5] zur Verfügung.
B. Alternativen zu NUnit
Ein weiteres Rahmenwerk ist CSUnit [6], das ähnlich ist
wie NUnit. Allerdings ist dessen Umfang an Assertbefehlen
etwas kleiner. Ein Tutorial sowie eine Anleitung zum Umstieg
von NUnit zu CSUnit findet man auf den Herstellerseiten.
Um Unit Tests fest in Visual Studio zu integrieren empfiehlt
sich das kommerzielle Produkt TestDriven.NET [7] oder das
freie Rahmenwerk VSNUnit [8].
In Visual Studio Team System ist ein Komponenten- und
Modultestframework bereits integriert. Dies zeigt, dass auch
Microsoft die Bedeutung von testgetriebener Entwicklung erkannt hat und als Verkaufsargument“ einsetzt. Voraussichtlich
”
werden alle kommenden Visual Studio Versionen die testgetriebene Entwicklung unterstützen.
L ITERATUR
[1] K. Beck, Test-driven development : by example, 9th ed. Boston, Mass.
[u.a.] : Addison-Wesley, 2006.
[2] ——, Extreme Programming Explained, 2nd ed. Boston, Mass. [u.a.]
: Addison-Wesley, 2006.
[3] M. C. Two, C. Poole, J. Cansdale, and G. Feldman, “NUnit
Rahmenwerk,” 2006. [Online]. Available: http://www.nunit.org/
[4] B. Knowles, J. Shore, and L. Khatskevitch, “NUnitASP,” 2005.
[Online]. Available: http://nunitasp.sourceforge.net/
[5] L. T. Maxon, “NUnitForms,” 2004. [Online]. Available: http:
//nunitforms.sourceforge.net/
[6] J. W. Anderson, P. Lawson, M. Renschler, and M. Lange, “csUnit,”
2006. [Online]. Available: http://www.csunit.org/
[7] J. Cansdale, “TestDriven.NET,” 2002-2006. [Online]. Available:
http://www.testdriven.net/
[8] J. Gehtland, “VSNUnit,” 2004. [Online]. Available: http://sourceforge.
net/projects/vsnunit/
[9] J. W. Newkirk and A. A. Vorontsov, Test-Driven Development in
Microsoft .NET. Microsoft Press, 2004.
[10] F. Westphal, “Testgetriebene Entwicklung,” 2002. [Online]. Available:
http://www.frankwestphal.de/TestgetriebeneEntwicklung.html
[11] B. Rumpe, “Extreme Programming - Back to Basics?” 2003. [Online].
Available: http://www4.in.tum.de/publ/papers/Rum01.pdf
[12] D. Wells, “Extreme Programming,” 2006. [Online]. Available: http:
//www.extremeprogramming.org/
[13] P. Provost, “Test-Driven Development in .NET,” 2003. [Online].
Available: http://www.codeproject.com/dotnet/tdd in dotnet.asp
[14] W. Stott and J. Newkirk, “Test-Driven C# - Improve the Design and
Flexibility of Your Project with Extreme Programming Techniques,”
2004. [Online]. Available: http://msdn.microsoft.com/msdnmag/issues/
04/04/ExtremeProgramming/default.aspx
[15] A. Hunt and D. Thomas, Pragmatic Unit Testing in C# with NUnit.
The Pragmatic Bookshelf, Raleigh, 2004.
[16] B. Hamilton, NUnit Pocket Reference. O’Reilly, Cambridge, 2004.