Laden...

Per Unit Test die Serialisierung/Deserialisierung meiner WCF Contract Klassen testen

Erstellt von Wax vor 6 Jahren Letzter Beitrag vor 6 Jahren 1.812 Views
Wax Themenstarter:in
731 Beiträge seit 2006
vor 6 Jahren
Per Unit Test die Serialisierung/Deserialisierung meiner WCF Contract Klassen testen

Hallo zusammen,

ich würde gerne per Unit Test die Serialisierung/Deserialisierung meiner WCF Contract Klassen testen.

Um die De-/Serialisierung zu testen, kommt folgende Hilfefunktion zum Einsatz:


public static T DataContractSerializationRoundTrip<T>(T obj, IEnumerable<Type> knownTypes = null)
        {
            T result = default(T);

            var serializer = new DataContractSerializer(obj.GetType());

            using (MemoryStream memoryStream = new MemoryStream())
            {
                serializer.WriteObject(memoryStream, obj);
                memoryStream.Position = 0;
                result = (T)serializer.ReadObject(memoryStream);
            }

            return result;
        }

Das funktioniert für alle meine Typen ohne Probleme.

Zur Laufzeit wirft mein WCF Service am Client allerdings die Fehlermeldung, dass ein nicht erwarteter Typ gefunden wurde und ich diesen doch bitte zur Liste der "KnownTypes" hinzufügen soll. Wenn ich das mache klappt es natürlich wieder, allerdings würde ich gerne etwas Prophylaxe betreiben und diese Fehler bereits per Unit Tests finden.

Wie kann ich also die tatsächliche Serialisierung "simulieren" für den Unit Test?

3.003 Beiträge seit 2006
vor 6 Jahren

Ich bin mir nicht sicher, was du in dem Codeschnipsel eigentlich testest. So, wie das aussieht, fühlst du dich genötigt, den DataContractSerializer von Hand zu testen....das ergibt so keinen Sinn, denn du möchtest nicht wissen, ob der Serialisierer serialisieren und deserialisieren kann (Hint: er kann), sondern ob DEINE Klassen, wenn sie serialisiert werden, so aussehen, wie du es erwartest.

Davon abgesehen zu deiner Fehlermeldung.
Wenn zur Entwurfszeit nicht klar ist, welchen konkreten Typ eine Eigenschaft haben wird, dann kannst du dem Serialisierer mit dem KnownType-Attribut einen Hinweis auf die Typen geben, die in Frage kommen:


//folgende Konstellation:
interface IExampleInterface { }
class ExampleImplementation : IExampleInterface { }
class AnotherImplementation : IExampleInterface { }

[DataContract]
[KnownType(typeof(ExampleImplementation))]
[KnownType(typeof(AnotherImplementation))]
class ExampleDto
{
    [DataMember]
    public IExampleInterface Property { get; set; }
}

Dasselbe Prinzip erfolgt bei anderen Konstellationen, wo der konkrete Typ nicht erkennbar ist:

  • Vererbung
  • object als Propertytyp
  • IExtensibleDataObject-Implementierungen

In fast allen Fällen hier, in denen der WCF-Dienst dann einen Fault produzierte, obwohl die Unittests durchgingen, war die Ursache nicht der Test, sondern lückenhafte Abdeckung mit TestCases oder fehlerhafte TestCases. Ich würde also als ersten Schritt einen TestCase konstruieren, der wegen des fehlenden KnownType-Attributes in einer SerializationException resultiert. Wenn du das hast, kannst du deine TestCases anpassen und dann deine DataContracts vervollständigen.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

Wax Themenstarter:in
731 Beiträge seit 2006
vor 6 Jahren

der wegen des fehlenden KnownType-Attributes in einer SerializationException resultiert. Wenn du das hast, kannst du deine TestCases anpassen und dann deine DataContracts vervollständigen

Genau das möchte ich erreichen und ich hatte gehofft das ein serialisieren mit dem DataContractSerializer die Exception werfen würde im Fehlerfall.

Ich stehe gerade auf dem Schlauch wie ich diese Exceptions herauskitzeln könnte.

ps: Der UnitTest dazu sieht so aus:


static Object[] DataContractTypes =
        {
            new Object[] {new ItemContainerDTO()},
            new Object[] {new ItemContainerDTO()},
            new Object[] {new DownloadDocumentDTO()},
            new Object[] {new UploadDocumentDTO()},
            new Object[] {new DocumentItemContainerDTO()},
            new Object[] {new DocumentResponseContainerDTO()},
            new Object[] {new ResponseContainerDTO()},
            new Object[] {new UploadDocumentResponseContainerDTO()}
        };


        [TestCaseSource("DataContractTypes")]
        public void ReflectionTest<T>(T source) where T : class
        {
            // Arrange
            ReflectionEqualityComparer<T> comparer = new ReflectionEqualityComparer<T>();

            // Act
            T deserializedObject = DataContractTestHelper.DataContractSerializationRoundTrip<T>(source);

            // Assert
            Assert.IsTrue(comparer.Equals(source, deserializedObject));
        }

3.003 Beiträge seit 2006
vor 6 Jahren

In deinen Testcases sind die Dto leer. Tu doch mal was rein. Am besten etwas, das einen Typ hat, der nicht bekannt gemacht wurde.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

3.003 Beiträge seit 2006
vor 6 Jahren

Beispiel:


[DataContract]
public class ExampleClass
{
    [DataMember]
    public object Property { get; set; }
}

class InternalUnknownClass
{
    public string Name { get; set; }
}

[TestFixture]
class TestClass
{
    IEnumerable<TestCaseData> TestCases
    {
         get 
         { 
              var obj = new ExampleClass { Property = new InternalUnknownClass() };
              yield return new TestCaseData(obj).SetName("UnknownType").Throws(typeof(SerializationException));
        }
    }

    [Test, TestCaseSource("TestCases")]
    public void TestKnownTypes(object dto)
    {
         var serializer = new DataContractSerializer(dto.GetType());
         using(var stream = new MemoryStream())
            serializer.WriteObject(stream, dto);
    }
}

Im Editor geschrieben, nach Bedarf um kleine Tippfehler erleichtern und anpassen 😉.

LaTino
EDIT: das Dto und die unbekannte Klasse (muss nicht intern sein) sollten natürlich nicht im Test deklariert sein...dann klappt's auch mit dem Nachbarn.

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

Wax Themenstarter:in
731 Beiträge seit 2006
vor 6 Jahren

Ich hatte die ganze Zeit einen falschen Typen verwendet. Jetzt bekomme ich mit meinem Code das gewünschte Verhalten, sprich es werden die Exceptions geworfen, falls ein Typ unbekannt ist.

Danke LaTino.

3.003 Beiträge seit 2006
vor 6 Jahren

Das sind meistens die Fälle, die einem dann bei Diskussionen um "WCF kann man nicht testen, live verhält sich das anders als im Test" unter die Nase gerieben werden 😉.

Btw, die TestCaseSource würde ich immer als IEnumerable<TestCaseData> deklarieren. Dann kann man, wie in meinem Beispiel, schön mit Namen und Ausnahmen und dem ganzen Gedöns arbeiten.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)