How to unit test if my object is really serializable?
Asked Answered
A

7

37

I am using C# 2.0 with Nunit Test. I have some object that needs to be serialized. These objects are quite complex (inheritance at different levels and contains a lot of objects, events and delegates).

How can I create a Unit Test to be sure that my object is safely serializable?

Alrzc answered 25/10, 2008 at 15:47 Comment(0)
H
17

I have this in some unit test here at 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);
}

Might help you... maybe other method can be better but this one works well.

Hermineherminia answered 25/10, 2008 at 15:47 Comment(3)
You need to be careful of NonSerialized (or transient in java) objects though, and those should be tested as part of your serialization and deserialization.Minda
@Minda there are no NonSerialized objects, only NonSerialized fields.Engorge
BinaryFormatter is obsolete in .Net6. Suggestions for .Net6? learn.microsoft.com/en-us/dotnet/fundamentals/…Apfelstadt
S
49

Here is a generic way:

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));
}
Sachiko answered 25/10, 2008 at 17:4 Comment(2)
works perfectly. Comparing the original to the Cloned copy is a great test of serialization as checking IsSerializable only checks the attribute of the class and not base class or other propertiesGangplank
If you change the signature of the clone method to public static T Clone<T>(T source) you get proper type inference for free.Maritsa
H
17

I have this in some unit test here at 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);
}

Might help you... maybe other method can be better but this one works well.

Hermineherminia answered 25/10, 2008 at 15:47 Comment(3)
You need to be careful of NonSerialized (or transient in java) objects though, and those should be tested as part of your serialization and deserialization.Minda
@Minda there are no NonSerialized objects, only NonSerialized fields.Engorge
BinaryFormatter is obsolete in .Net6. Suggestions for .Net6? learn.microsoft.com/en-us/dotnet/fundamentals/…Apfelstadt
S
16

In addition to the test above - which makes sure the serializer will accept your object, you need to do a round-trip test. Deserialize the results back to a new object and make sure the two instances are equivalent.

Sarette answered 25/10, 2008 at 15:54 Comment(0)
A
4

Probably a bit late in the day, but if you are using the FluentAssertions library, then it has custom assertions for XML serialization, binary serialization, and data contract serialization.

theObject.Should().BeXmlSerializable();
theObject.Should().BeBinarySerializable();
theObject.Should().BeDataContractSerializable();

theObject.Should().BeBinarySerializable<MyClass>(
    options => options.Excluding(s => s.SomeNonSerializableProperty));
Angelika answered 3/7, 2017 at 3:49 Comment(0)
P
2

serialize the object (to memory or disk), deserialize it, use reflection to compare the two, then run all of the unit tests for that object again (except serialization of course)

this assumes that your unit tests can accept an object as a target instead of making their own

Pad answered 25/10, 2008 at 16:19 Comment(2)
I always comment on a down vote. You don't need to run tests again if the two have equivalent data. Beyond that when comparing data you have to understand that sometimes data changes when serializing/deserializing and need a way of expressing this (think about things that use the memento pattern like serializing a form the two may be equivalent after even though all the data is not the same)Dejong
@Greg: serilization is supposed to be identical, but as you point out may have fluctuations. The serialization routines and the comparison routines may also be incomplete or flawed, hence the recommendation to run all of the unit tests again. If they all pass on the reconstituted object, it's good to go. If they don't, this will indicate places where rehydration (and/or comparison) failed.Pad
F
2

Here is a solution that recursively uses IsSerializable to check that the object and all its properties are Serializable.

    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);
        }
    }
Fussell answered 1/11, 2010 at 18:23 Comment(1)
The generic arguments don't need to be serializable for a type to be, for example ObjectComparer<NonSerialiableType> is serializable.Blab
B
1

Unfortunately, you can't really test for this. Imagine this case:

[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
}
Blab answered 16/12, 2010 at 19:9 Comment(1)
how can you assign DerivedBar to MyBar?Coomer

© 2022 - 2024 — McMap. All rights reserved.