UnitTests 2: Assert and Friends

6. Juni 2012

Im letzten Beitrag habe ich mich zum Thema Assert-Methoden in UnitTests und deren Ausgaben ausgelassen. Assert kennt jeder der schon mal einen UnitTest geschrieben hat. Aber auch alle Methoden? Und was ist mit anderen Prüf-Klassen? (Wie? Das wussten Sie gar nicht…?)

 

Assert

Tatsächlich hat Assert ein paar Exoten zu bieten, die nicht jedem sofort geläufig sind. Ich habe mir mal die Mühe gemacht, diese mit ihren jeweiligen Ausgabe im Fehlerfall in einen Test zu packen:

image

Der Name des Tests sollte deutlich machen, welche Methode aufgerufen wird.

Bereits AreEqual() – obwohl recht geläufig – ist einige Anmerkungen wert. Hier gibt es unterschiedliche Überladungen:

  • Bei object wird ein Object.Equals() verwendet.
  • Bei string wird ein string.Compare() verwendet und man kann Groß/Kleinschreibung ignorieren.
  • Für single und double gibt es Varianten, bei denen ein Delta als Schwellwert für die Ungleichheit übergeben werden kann. Auf diese Art lassen sich Darstellungs- und Berechnungsungenauigkeiten von Fließkommazahlen abfangen. So kann dann etwa 1,0 als erwarteter und 1,00000000037 als berechneter Wert als erfolgreich durchgehen.

AreSame unterscheidet sich von AreEqual() dadurch, dass nicht Object.Equals(), sondern Object.ReferenceEquals() verwendet wird – es wird also auf identische Objekte geprüft.

Kleiner Test am Rande: Was ist das Ergebnis folgender beider Testmethoden:

   1: [TestMethod]

   2: public void Test1()

   3: {

   4:     var expected= "Test";

   5:     var actual= "Test";

   6:  

   7:     Assert.AreEqual(expected, actual);

   8:     Assert.AreSame(expected, actual);

   9: }

  10:  

  11: [TestMethod]

  12: public void Test2()

  13: {

  14:     var expected= "TEST";

  15:     var actual= "Test".ToUpper();

  16:  

  17:     Assert.AreEqual(expected, actual);

  18:     Assert.AreSame(expected, actual);

  19: }

Welcher wird grün, welcher rot?

Bleibt noch IsInstanceOfType(): Damit lässt sich prüfen, ob ein Objekt einen bestimmten Typ hat. Das schließt Basisklassen und Interfaces ein. Eine typische Prüfung für Fällen, in den Objekte dynamisch erzeugt werden. Hat man die Methode nicht im Hinterkopf, verwendet man vermutlich eher Assert.IsTrue(value is IComparable) – und verliert damit die schön aufbereitete Fehlerausgabe im Beispiel.

 

Soviel zu Assert, jetzt zu seinen Geschwistern, die nach meiner Erfahrung kaum jemand kennt: StringAssert und CollectionAssert.

StringAssert

Speziell für String-Analysen bietet StringAssert ein paar nette Überladungen, die spezielle Prüfungen einfacher machen können. Auch hier wieder der Testlauf:

image

Contains(), StartsWith() und EndsWith() dürften selbsterklärend sein. Und Matches() bietet den Vergleich auf Basis von Regular Expressions an.

CollectionAssert

Der Letzte im Bunde hat durchaus einige Perlen im Gepäck: CollectionAssert. Wer schon mal Listen nach verschiedenen Kriterien vergleichen musste, wird diese Klasse schätzen:

image 

AreEqual() vergleicht, ob alle Elemente in den Listen übereinstimmen, inklusive ihrer Reihenfolge. AreEquivalent() hingegen ignoriert die Reihenfolge, das heißt die Elemente können ungeordnet vorliegen. Und dabei wird auch der Sonderfall berücksichtigt, dass ein Objekt mehrfach in der Liste auftauchen kann; es muss dann in der gleichen Anzahl auch in der anderen Liste vorliegen.

AreEqual(), AreEquivalent() und AllItemsAreUnique() arbeiten beim Vergleich der einzelnen Elemente wieder über object.Equals() (einzig AreEqual() hat eine Überladung, die ein IComparer übernimmt). Bei Listen mit beliebigen Elementen mag das zunächst eine Einschränkung sein. Andererseits ist es gerade bei Listen mit LINQ vergleichsweise einfach, die beiden Ausgangslisten in Listen zu übersetzen, die das identifizierende Objekt beinhalten, etwa:

   1: [TestMethod]

   2: public void TestGetBalanceSheets()

   3: {

   4:     var data = new DataAccess();

   5:  

   6:     // we know the test user and ID!

   7:     var result = data.GetBalanceSheets(100);

   8:     

   9:     var expectedIDs = new int[] { 102, 101 };

  10:     CollectionAssert.AreEquivalent(expectedIDs, result.Select(b => b.ID).ToArray());

  11: }

Nebenbei: Wer beim letzten Beitrag aufgepasst hat, sollte jetzt laut aufschreien, weil ich result nicht auf null geprüft habe Winking smile.

 

Fazit

Ich hoffe, damit einige Hinweise gegeben zu haben, dass das Prüfen von Ergebnisse nicht bei den typischen Methoden von Assert endet. Wer bei den hier vorgestellten Prüfmethoden noch nicht fündig wird, weil sein Projekt besondere Anforderungen hat, der sollte sich vielleicht seine eigenen Prüf-Klassen schreiben, die insbesondere eine klare inhaltliche Aussage im Fehlerfall machen. Mögliche Kandidaten kann ich mir genügend vorstellen:

  • Vergleich von Objekt-Hierarchien anhand der Properties
  • Vergleiche von Streams
  • Prüfung von XML-Dokumenten, etwa anhand von XPath-Ausdrücken

Und vielleicht finde ich dann im nächsten Projekt Tests die fehlschlagen und mir aussagekräftige Hinweise geben. Obwohl… Tests die grün bleiben haben auch was für sich… Winking smile