2009-07-09 8 views
14

Ich habe eine Klasse, die eine Reihe von Objekten (mit XML-Serialisierung) serialisiert, die ich Unit-Test wollen.Gibt es einen Punkt Unit Unit Serialisierung testen?

Mein Problem ist, es fühlt sich an, als würde ich die .NET-Implementierung der XML-Serialisierung testen, anstatt irgendetwas Nützliches. Ich habe auch ein leichtes Huhn-und-Ei-Szenario, in dem ich, um den Reader zu testen, eine vom Writer produzierte Datei dazu benötige.

denke ich, die Fragen (es 3 ist, aber sie alle beziehen sich) Ich bin auf der Suche letztlich für Feedback zu sind:

  1. Ist es möglich, den Writer zu testen, ohne den Reader?
  2. Was ist die beste Strategie zum Testen des Lesers (XML-Datei? Mocking mit Aufnahme/Wiedergabe)? Ist es der Fall, dass Sie wirklich nur Eigenschaftswerte der Objekte testen, die deserialisiert wurden?
  3. Was ist die beste Strategie zum Testen des Schreibers!

Hintergrund Informationen über XML-Serialisierung

Ich bin kein Schema verwenden, so dass alle XML-Elemente und Attribute passen die Eigenschaften der Objekte. Da es kein Schema gibt, werden Tags/Attribute, die nicht mit den Eigenschaften der einzelnen Objekte übereinstimmen, vom XmlSerializer einfach ignoriert (der Wert der Eigenschaft ist also null oder Standard). Hier ist ein Beispiel

<MyObject Height="300"> 
    <Name>Bob</Name> 
    <Age>20</Age> 
<MyObject> 

Karte würde

public class MyObject 
{ 
    public string Name { get;set; } 
    public int Age { get;set; } 

    [XmlAttribute] 
    public int Height { get;set; } 
} 

und umgekehrt. Wenn das Objekt in den unteren Bereich geändert wird, wird die XML-Datei immer noch erfolgreich deserialisiert, aber FirstName ist leer.

public class MyObject 
{ 
    public string FirstName { get;set; } 
    public int Age { get;set; } 

    [XmlAttribute] 
    public int Height { get;set; } 
} 

eine ungültige XML-Datei würde richtig, damit die Einheit deserialisieren Test bestehen würde, wenn man Aussagen über den Werten des MyObject lief.

Antwort

19

Ich würde argumentieren, dass es wichtig ist, Unit Test Serialisierung wenn ist es äußerst wichtig, dass Sie Daten zwischen den Versionen lesen können. Und Sie müssen Test mit "bekannt guten" Daten (d. H. Es ist nicht ausreichend, einfach Daten in der aktuellen Version zu schreiben und dann wieder zu lesen).

Sie erwähnen, dass Sie kein Schema haben ... warum nicht eines generieren? Entweder von Hand (es ist nicht sehr schwer), oder mit xsd.exe. Dann haben Sie etwas als Vorlage zu verwenden, und Sie können dies nur mit XmlReader überprüfen. Ich mache ein Los der Arbeit mit XML-Serialisierung im Moment, und es ist viel einfacher, das Schema zu aktualisieren, als es sich darum zu kümmern, ob ich die Daten richtig mache.

Sogar XmlSerializer kann komplex werden; Dies gilt insbesondere, wenn Sie Unterklassen ([XmlInclude]), benutzerdefinierte Serialisierung (IXmlSerializable) oder nicht standardmäßige XmlSerializer Konstruktion (übergeben zusätzliche Metadaten zur Laufzeit an den Ctor).Eine andere Möglichkeit ist die kreative Verwendung von [XmlIngore], [XmlAnyAttribute] oder [XmlAnyElement]; zum Beispiel könnten Sie unerwartete Daten für Hin- und Rückfahrt (nur) in der Version X, sondern speichern sie in einer bekannten Eigenschaft in Version Y.


Mit Serialisierung im Allgemeinen unterstützen:

Der Grund ist einfach: Sie können die Daten brechen! Wie schlimm das ist, hängt vom Serializer ab; zum Beispiel mit BinaryFormatter (und ich weiß, die Frage XmlSerializer ist), einfaches Ändern von:

public string Name {get;set;} 

zu

private string name; 
public string Name { 
    get {return name;} 
    set {name = value; OnPropertyChanged("Name"); } 
} 

enough to break serialization sein könnte, als der Feldname (und BinaryFormatter lieben Felder) geändert hat .

Es gibt andere Fälle, in denen Sie die Daten versehentlich umbenennen könnten (selbst in Contract-basierten Serialisierern wie XmlSerializer/DataContractSerializer). In solchen Fällen können Sie normalerweise die Drahtbezeichner außer Kraft setzen (zum Beispiel [XmlAttribute("name")] usw.), aber es ist wichtig, dies zu überprüfen!

Letztendlich kommt es darauf an: Ist es wichtig, dass Sie alte Daten lesen können? Es ist normalerweise; also verschicken Sie es nicht einfach ... beweisen Sie, die Sie können.

+0

Es hatte ursprünglich ein Schema, und lesen + schreiben wurde von meiner eigenen Klasse, die das Objekt Diagramm geschrieben. Ich habe dann (1 Jahr später!) Entdeckt, dass die ganze Arbeit vom XmlSerializer erledigt werden konnte. In Bezug auf die Rückwärtskompatibilität würde ich sagen, dass das XML an die Assembly-Version gebunden wäre, mit der es geschrieben wurde. Wenn Sie also der letzten Version der Assembly XML-Dateien aus früheren Versionen gaben, besteht die Möglichkeit, dass sich das Objektmodell geändert hat und nicht mehr übereinstimmt. Ich bin mir nicht sicher, dass das jemals passieren wird, aber ich kann nicht sehen, wie (Fortsetzung) –

+0

Sie ein älteres Format deserialisieren (wie Jon erwähnt), ohne dass es immer bricht und es manuell mit durchführen muss ein XmlReader? –

+0

XmlSerializer löscht unerwartete Daten ohne Fehler, oder Sie können die '[XmlAny *]' verwenden - so gibt es Möglichkeiten, ein Objekt * teilweise * zu deserialisieren (und die anderen Daten von den "beliebigen" Requisiten zu erhalten). –

7

Für mich ist dies absolut in der Kategorie nicht zu stören. Ich teste nicht meine Werkzeuge. Wenn Sie jedoch Ihre eigene Serialisierungsklasse geschrieben haben, dann testen Sie sie auf jeden Fall.

26

Müssen Sie rückwärtskompatibel sein? Wenn dies der Fall ist, kann es sich lohnen, Unit-Tests von Dateien zu erstellen, die von alten Versionen erzeugt wurden und die noch durch neue Versionen deserialisiert werden können.

Other than that, wenn Sie jemals etwas „interessant“ einführen kann einen Unit-Test wert sein zu überprüfen können Sie serialisieren und deserialisieren nur um sicherzustellen, dass Sie nicht etwas flippig mit einer Nur-Lese-Eigenschaft zu tun usw.

+1

Wir könnten hinzufügen und entfernen (Blatt) Eigenschaften in der Zukunft, aber es wird sich nicht dramatisch ändern –

+3

Ich würde sagen, das ist immer noch genug einer Änderung, um es sich zu testen lohnt. –

0

Wenn es nichts, was Sie tun können, die Art und Weise Ihre Klasse serialisiert zu ändern, dann sind testen Sie .NET-Implementierung von XML-Serialisierung ;-)

0

Wenn das Format der serialisierten XML-Angelegenheiten, dann müssen Sie testen die Serialisierung. Wenn es wichtig ist, dass Sie es deserialisieren können, müssen Sie die Deserialisierung testen.

5

Wenn Sie sicherstellen möchten, dass die Serialisierung Ihrer Objekte nicht bricht, dann auf jeden Fall Unit-Test.Wenn Sie die MSDN-Dokumentation für die XMLSerializer Klasse lesen:

Die XmlSerializer können nicht serialisiert oder deserialisiert die folgenden:

Arrays von Arraylist
Arrays von Liste <T>

Es gibt auch ein eigentümliches Problem mit enums, die als unsigned long deklariert sind. Außerdem werden alle Objekte, die als [Obsolete] markiert sind, ab .Net 3.5 nicht serialisiert. Wenn Sie eine Reihe von Objekten haben, die serialisiert werden, mag das Testen der Serialisierung seltsam erscheinen, aber es braucht nur jemand, der die serialisierten Objekte bearbeitet, um eine der nicht unterstützten Bedingungen für die Unterbrechung der Serialisierung aufzunehmen.

Im Endeffekt testen Sie die XML-Serialisierung nicht, Sie testen, ob Ihre Objekte serialisiert werden können. Gleiches gilt für die Deserialisierung.

0

zu sehen, wie man kann nicht wirklich Serialisierung zu beheben, sollten Sie es nicht testen - stattdessen sollten Sie Ihren eigenen Code und die Art und Weise testen es mit dem Serialisierungsmechanismus in Wechselwirkung tritt. Zum Beispiel müssen Sie möglicherweise die Struktur der Daten, die Sie serialisieren, in Einzelteilen testen, um sicherzustellen, dass niemand versehentlich ein Feld oder etwas ändert.

Apropos, ich habe vor kurzem eine Praxis, wo ich solche Dinge unter Kompilierzeit eher als während der Ausführung von Komponententests angenommen. Es ist ein bisschen langweilig, aber ich habe eine Komponente, die die AST durchlaufen kann, und dann kann ich es in einer T4-Vorlage lesen und viele #error Nachrichten schreiben, wenn ich etwas treffe, das nicht da sein sollte.

+0

Sorry, das ist platt-falsch. Sie können viele Details der XML-Serialisierung stark beeinflussen, indem Sie den zu serialisierenden Klassen Attribute hinzufügen. –

+1

Könnte eine Frage der Interpretation sein - aber für mich testet "die Art und Weise, wie es mit dem Serialisierungsmechanismus * interagiert", dass getestet wird, ob die von dir gesetzten Attribute so funktionieren, wie du denkst. Sie testen nicht, ob eine Eigenschaft serialisiert werden kann. Sie testen, ob Ihre Konfiguration von ** dieser Eigenschaft ** korrekt konfiguriert wurde. – Bevan

1

Ich habe dies in einigen Fällen getan ... nicht die Serialisierung als solche zu testen, aber einige "bekannt gute" XML-Serialisierungen und dann laden sie in meine Klassen und prüfen, ob alle Eigenschaften (wie zutreffend) haben erwartete Werte.

Das wird nichts für die erste Version testen ... aber wenn die Klassen jemals weiterentwickeln, weiß ich, dass ich alle brechenden Änderungen im Format fangen werde.

2

Nach meiner Erfahrung ist es definitiv wert, zu tun, besonders wenn das XML als XML-Dokument vom Verbraucher verwendet wird. Zum Beispiel muss der Verbraucher möglicherweise jedes Element im Dokument haben, um entweder eine Nullprüfung von Knoten beim Query zu vermeiden oder um eine Schema-Validierung zu bestehen.

Standardmäßig wird der XML-Serializer Eigenschaften mit einem Nullwert auslassen, es sei denn, Sie fügen das Attribut [XmlElement (IsNullable = true)] hinzu. In ähnlicher Weise müssen Sie möglicherweise allgemeine Listeneigenschaften an Standardarrays mit einem XMLArray-Attribut umleiten.

Wie ein anderer Mitwirkender sagte, wenn sich das Objekt im Laufe der Zeit ändert, müssen Sie kontinuierlich überprüfen, ob die Ausgabe konsistent ist. Es schützt Sie auch davor, dass sich der Serializer selbst ändert und nicht abwärtskompatibel ist, obwohl Sie hoffen, dass dies nicht geschieht.

Also für alles andere als triviale Anwendungen, oder wo die obigen Überlegungen irrelevant sind, ist es die Mühe der Unit-Tests es wert.

2

Es gibt viele Arten, mit denen die Serialisierung nicht umgehen kann usw. Auch wenn Sie Ihre Attribute falsch haben, ist es üblich, eine Ausnahme zu erhalten, wenn Sie versuchen, die XML zurück zu lesen.

Ich neige dazu, einen Beispielbaum der Objekte zu erstellen, die mit mindestens einem Beispiel jeder Klasse (und Unterklasse) serialisiert werden können. Dann serialisieren Sie den Objektbaum mindestens zu einem Stringstream und lesen Sie ihn dann aus dem Stringstream zurück.

Sie werden erstaunt sein, wie oft das Problem auftaucht und ich muss warten, bis die Anwendung gestartet ist, um das Problem zu finden. Bei dieser Ebene der Komponententests geht es eher darum, die Entwicklung zu beschleunigen, anstatt die Qualität zu erhöhen. Ich würde das also nicht für die Serialisierung tun. Wie andere Leute gesagt haben, wenn Sie in der Lage sein müssen, Daten zu lesen, die von alten Versionen Ihrer Software gespeichert wurden, sollten Sie lieber eine Reihe von Beispieldatendateien für jede ausgelieferte Version behalten und Tests durchführen, um zu bestätigen, dass Sie immer noch können Lese sie.Dies ist schwieriger, als es zunächst scheint, da sich die Bedeutung von Feldern auf einem Objekt zwischen den Versionen ändern kann. Es reicht also nicht aus, das aktuelle Objekt aus einer alten serialisierten Datei zu erstellen, sondern Sie müssen prüfen, ob die Bedeutung identisch ist da war es die Version der Software, die die Datei speicherte. (Setzen Sie jetzt ein Versionsattribut in Ihr Root-Objekt!)

2

Ich stimme Ihnen zu, dass Sie die .NET-Implementierung mehr testen werden, als Sie Ihren eigenen Code testen werden. Aber wenn Sie das wollen (vielleicht trauen Sie der .NET-Implementierung nicht) :), könnte ich Ihre drei Fragen wie folgt angehen.

  1. Ja, es ist sicherlich möglich, den Schreiber ohne den Leser zu testen. Verwenden Sie den Writer, um das Beispiel (20-jähriger Bob) zu serialisieren, das Sie einem MemoryStream zur Verfügung gestellt haben. Öffnen Sie den MemoryStream mit einem XmlDocument. Stellen Sie sicher, dass der Stammknoten "MyObject" heißt. Assert es hat ein Attribut namens "Höhe" mit dem Wert "300". Assert gibt es ein "Name" -Element, das einen Textknoten mit dem Wert "Bob" enthält. Assert gibt es ein "Age" -Element, das einen Textknoten mit dem Wert "20" enthält.

  2. Tun Sie einfach den umgekehrten Prozess von # 1. Erstellen Sie ein XmlDocument aus der 20 Jahre alten XML-Zeichenfolge von Bob. Deserialisieren Sie den Stream mit dem Reader. Stellen Sie sicher, dass die Name-Eigenschaft gleich "Bob" ist. Stellen Sie sicher, dass die Age-Eigenschaft gleich 20 ist. Sie können beispielsweise einen Testfall mit unbedeutendem Leerzeichen oder einfachen Anführungszeichen anstelle von doppelten Anführungszeichen hinzufügen, um genauer zu sein.

  3. Siehe # 1. Sie können es erweitern, indem Sie Dinge hinzufügen, die Sie für knifflige "Kanten" -Fälle halten, von denen Sie denken, dass sie diese brechen könnten. Namen mit verschiedenen Unicode-Zeichen. Extra lange Namen. Leere Namen. Negatives Alter

    usw.
1

Wir tun Akzeptanz Prüfung unserer Serialisierung statt Unit-Tests.

Dies bedeutet, dass unsere Akzeptanztester das XML-Schema oder wie in Ihrem Fall ein Beispiel-XML verwenden und ihre eigene serialisierbare Datenübertragungsklasse neu erstellen.

Wir verwenden dann NUnit, um unseren WCF-Dienst mit diesem Reinraum-XML zu testen.

Mit dieser Technik haben wir viele, viele Fehler identifiziert. Zum Beispiel, wo wir den Namen des .NET-Mitglieds geändert haben und vergessen haben, ein [XmlElement]-Tag mit einer Name =-Eigenschaft hinzuzufügen.

3

Ja, solange das, was getestet werden muss, durch ein bisschen Eingreifen getestet wurde.

Die Tatsache, dass Sie in erster Linie serialisieren und deserialisieren, bedeutet, dass Sie wahrscheinlich Daten mit der "Außenwelt" austauschen - der Welt außerhalb der .NET-Serialisierungsdomäne. Daher sollten Ihre Tests einen Aspekt haben, der außerhalb dieser Domäne liegt. Es ist nicht in Ordnung, den Writer mit dem Reader zu testen und umgekehrt.

Es geht nicht nur darum, ob Sie gerade die .NET-Serialisierung/Deserialisierung testen würden; Sie müssen Ihre Schnittstelle mit der Außenwelt testen - dass Sie XML im erwarteten Format ausgeben können und dass Sie XML im erwarteten Format korrekt konsumieren können.

Sie sollten über statische XML-Daten verfügen, die zum Vergleichen mit der Serialisierungsausgabe und zum Verwenden als Eingabedaten für die Deserialisierung verwendet werden können.

Angenommen, Sie die Aufgabe, Notizen und die Noten wieder auf den gleichen Typ geben Lesen:

 
You - Bob, I want you to jot down the following: "small yellow duck." 
Bob - OK, got it. 
You - Now, read it back to me. 
Bob - "small yellow duck" 

Nun, was wir hier getestet haben? Kann Bob wirklich schreiben? Hat Bob überhaupt etwas geschrieben oder hat er sich die Wörter gemerkt? Kann Bob tatsächlich lesen? - seine eigene Handschrift? Was ist mit der Handschrift einer anderen Person? Wir haben keine Antworten auf diese Fragen.

Lassen Sie uns nun Alice, um das Bild vorstellen:

 
You - Bob, I want you to jot down the following: "small yellow duck." 
Bob - OK, got it. 
You - Alice, can you please check what Bob wrote? 
Alice - OK, he's got it. 
You - Alice, can you please jot down a few words? 
Alice - Done. 
You - Bob, can you please read them? 
Bob - "red fox" 
Alice - Yup, that sounds right. 

Wir wissen jetzt, mit Sicherheit, dass Bob und lesen richtig schreiben kann - solange wir völlig Alice vertrauen können. Statische XML-Daten (idealerweise anhand eines Schemas getestet) sollten ausreichend vertrauenswürdig sein.