How to unit test if my object is really serializable?

20,603

Solution 1

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.

Solution 2

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));
}

Solution 3

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.

Solution 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));

Solution 5

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);
        }
    }
Share:
20,603
Pokus
Author by

Pokus

Updated on October 18, 2020

Comments

  • Pokus
    Pokus over 3 years

    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?

  • Egwor
    Egwor over 15 years
    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.
  • Catch22
    Catch22 about 14 years
    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 properties
  • erikkallen
    erikkallen over 13 years
    The generic arguments don't need to be serializable for a type to be, for example ObjectComparer<NonSerialiableType> is serializable.
  • Greg Young
    Greg Young over 13 years
    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)
  • Steven A. Lowe
    Steven A. Lowe over 13 years
    @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.
  • default
    default over 11 years
    how can you assign DerivedBar to MyBar?
  • Mishax
    Mishax almost 11 years
    @Egwor there are no NonSerialized objects, only NonSerialized fields.
  • fabsenet
    fabsenet over 7 years
    If you change the signature of the clone method to public static T Clone<T>(T source) you get proper type inference for free.