How to unit test if my object is really serializable?
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);
}
}
Pokus
Updated on October 18, 2020Comments
-
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 over 15 yearsYou 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 about 14 yearsworks 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 over 13 yearsThe generic arguments don't need to be serializable for a type to be, for example
ObjectComparer<NonSerialiableType>
is serializable. -
Greg Young over 13 yearsI 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 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 over 11 yearshow can you assign
DerivedBar
toMyBar
? -
Mishax almost 11 years@Egwor there are no NonSerialized objects, only NonSerialized fields.
-
fabsenet over 7 yearsIf you change the signature of the clone method to
public static T Clone<T>(T source)
you get proper type inference for free.