2008-10-25 3 views
29

Ich benutze C# 2.0 mit Nunit Test. Ich habe ein Objekt, das serialisiert werden muss. Diese Objekte sind ziemlich komplex (Vererbung auf verschiedenen Ebenen und enthält viele Objekte, Ereignisse und Delegaten).Wie testen, ob mein Objekt wirklich serialisierbar ist?

Wie kann ich einen Komponententest erstellen, um sicherzustellen, dass mein Objekt sicher serialisierbar ist?

Antwort

13

Ich habe dies in einem gewissen Unit-Test hier bei Job:

MyComplexObject dto = new MyComplexObject(); 
MemoryStream mem = new MemoryStream(); 
BinaryFormatter b = new BinaryFormatter(); 
try 
{ 
    b.Serialize(mem, dto); 
} 
catch (Exception ex) 
{ 
    Assert.Fail(ex.Message); 
} 

Ihnen helfen könnte ... vielleicht andere Methode kann besser sein, aber diese funktioniert gut.

+0

Sie müssen jedoch vor nicht serialisierten Objekten (oder transienten in Java) aufpassen, und diese sollten als Teil Ihrer Serialisierung und Deserialisierung getestet werden. – Egwor

+0

@Egwor Es gibt keine nicht serialisierten Objekte, nur nicht serialisierte Felder. – Mishax

13

Zusätzlich zu dem obigen Test - der dafür sorgt, dass der Serializer Ihr Objekt akzeptiert, müssen Sie einen Round-Trip-Test durchführen. Deserialisieren Sie die Ergebnisse in ein neues Objekt und stellen Sie sicher, dass die beiden Instanzen äquivalent sind.

3

Serialisieren das Objekt (in dem Speicher oder Festplatte), deserialisieren es, die beide, dann Lauf alle der Komponententests für das Objekt wieder (außer Serialisierung natürlich) Verwendung Reflexion

zu vergleichen

Dies setzt voraus, dass Ihre Unit-Tests können ein Objekt als Ziel akzeptieren, anstatt ihre eigenen machen

+0

Ich komme immer auf eine Down-Abstimmung. Sie müssen die Tests nicht erneut ausführen, wenn die beiden über gleichwertige Daten verfügen. Darüber hinaus müssen Sie beim Vergleichen von Daten verstehen, dass sich Daten beim Serialisieren/Deserialisieren manchmal ändern und eine Art des Ausdrückens benötigen (denken Sie an Dinge, die das Memento-Muster wie das Serialisieren eines Formulars verwenden), obwohl die Daten nicht identisch sind das gleiche) –

+1

@Greg: Serilisierung soll identisch sein, aber wie Sie darauf hinweisen, können Schwankungen haben. Die Serialisierungsroutinen und die Vergleichsroutinen können auch unvollständig oder fehlerhaft sein, daher die Empfehlung, alle Komponententests erneut auszuführen. Wenn sie alle das wiederhergestellte Objekt weitergeben, ist es gut zu gehen. Wenn dies nicht der Fall ist, zeigt dies Orte an, an denen Rehydration (und/oder Vergleich) fehlgeschlagen ist. –

40

Hier ist eine generische Art und Weise:

public static Stream Serialize(object source) 
{ 
    IFormatter formatter = new BinaryFormatter(); 
    Stream stream = new MemoryStream(); 
    formatter.Serialize(stream, source); 
    return stream; 
} 

public static T Deserialize<T>(Stream stream) 
{ 
    IFormatter formatter = new BinaryFormatter(); 
    stream.Position = 0; 
    return (T)formatter.Deserialize(stream); 
} 

public static T Clone<T>(object source) 
{ 
    return Deserialize<T>(Serialize(source)); 
} 
+0

funktioniert perfekt. Das Vergleichen des Originals mit der geklonten Kopie ist ein großer Test der Serialisierung, da das Überprüfen von IsSerializable nur das Attribut der Klasse und nicht die Basisklasse oder andere Eigenschaften überprüft. – Catch22

+1

Bitte korrigieren Sie den Tippfehler und ich gebe meine Stimme ab. :) – jpbochi

+0

Wenn Sie die Signatur der Klon-Methode in 'public static T Clone (T source)' ändern, erhalten Sie die richtige Typ-Inferenz kostenlos. – fabsenet

2

Hier ist eine Lösung, die IsSerializable rekursiv verwendet, dass das ob zu überprüfen Alle Eigenschaften sind serialisierbar.

private static void AssertThatTypeAndPropertiesAreSerializable(Type type) 
    { 
     // base case 
     if (type.IsValueType || type == typeof(string)) return; 

     Assert.IsTrue(type.IsSerializable, type + " must be marked [Serializable]"); 

     foreach (var propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) 
     { 
      if (propertyInfo.PropertyType.IsGenericType) 
      { 
       foreach (var genericArgument in propertyInfo.PropertyType.GetGenericArguments()) 
       { 
        if (genericArgument == type) continue; // base case for circularly referenced properties 
        AssertThatTypeAndPropertiesAreSerializable(genericArgument); 
       } 
      } 
      else if (propertyInfo.GetType() != type) // base case for circularly referenced properties 
       AssertThatTypeAndPropertiesAreSerializable(propertyInfo.PropertyType); 
     } 
    } 
+0

Die generischen Argumente müssen nicht serialisierbar sein, damit ein Typ definiert werden kann. Beispiel: "ObjectComparer " ist serialisierbar. – erikkallen

1

Leider können Sie nicht wirklich dafür testen. Stellen Sie sich vor, diesen Fall:

[Serializable] 
class Foo { 
    public Bar MyBar { get; set; } 
} 

[Serializable] 
class Bar { 
    int x; 
} 

class DerivedBar : Bar { 
} 

public void TestSerializeFoo() { 
    Serialize(new Foo()); // OK 
    Serialize(new Foo() { MyBar = new Bar() }; // OK 
    Serialize(new Foo() { MyBar = new DerivedBar() }; // Boom 
} 
+0

Wie können Sie "DerivedBar" zu "MyBar" zuordnen? – Default

+1

Typo. Fest..... – erikkallen

0

ein bisschen spät am Tag Wahrscheinlich, aber wenn Sie die FluentAssertions Bibliothek verwenden, dann hat es benutzerdefinierte Behauptungen für XML-Serialisierung, binäre Serialisierung und Datenvertrag Serialisierung.