One After the Other

That is an enigmatic title to write about serialization. Well, sort of, as one of the meanings of the word refers to that concept.
But writing about successions is not my intention. My intention is to write about the technique that allows objects to be persisted to another medium different from their native memory space and more specifically on how to test serialization of objects.

Freeze!

That is kind of what the values on an object do when it gets serialized. The values of the properties (the state of the object) are saved to the serialization medium (a stream) hoping they will be reconstituted into another instance of the type.

Once the type developer decides that his/her creation needs some flavor of serialization (the .NET framework offers some flavors on its own and third party libraries expand the choice) comes the time in which tests are written to either guide the implementation or check it against some assumptions.

In my limited experience designing serializable types I have faced several scenarios that I needed to test:

  1. check that the type can be successfully serialized and de-serialized
  2. check that the serialized form of an object contains the same values as the original
  3. provide examples of the serialization format

Return Ticket, Please

The first two scenarios can be tested by serializing the type, then de-serializing it, and comparing the values to the original object (the one that was serialized in the first place). Depending of the flavor of serialization, different objects are involved in the serialization sprinkling some I/O over the mixture.

The serialization target is, more often than not, rather unimportant, as so are the mechanics of the serialization flavor. Remember, we are not testing serialization per se, we are checking that our type can be serialized and the state is maintained. Once we have taken from the picture the repetitive and unimportant code, we can use an abstraction of the process of serializing and de-serializing an object to a stream. And that abstraction is provided by IRoundtripSerializer, included in Testing.Commons.

public interface IRoundtripSerializer : IDisposable
{
string Serialize(T toSerialize);
T Deserialize();
}

For that abstraction, some implementations are provided for popular types of serialization provided by the .NET Framework: BinaryRoundtripSerializer, XmlRoundtripSerializer, DataContractRoundtripSerializer and JsonRoundtripSerializer. But such types only implement the mechanics. How about actual checking the properties of the de-serialized object? For that purpose the SerializationConstraint, provided by Testing.Commons.NUnit can be used. Its usage is really simple, just use the Must extension to choose the flavor and provide constraints to be used over the de-serialized instance:

Assert.That(new Serializable { D = 3m, S = "s"},
Must.Be.BinarySerializable(
Has.Property("S").EqualTo("s")
.And.Property("D").EqualTo(3m)))
Assert.That(Serializable.DataContractString("s", 3m),
Must.Be.DataContractDeserializable<Serializable>(
Has.Property("S").EqualTo("s")
.And.Property("D").EqualTo(3m)));
Assert.That(Serializable.DataContractString("s", 3m),
Must.Be.XmlSerializable<Serializable>(
Has.Property("S").EqualTo("s")
.And.Property("D").EqualTo(3m)));
Assert.That(Serializable.DataContractString("s", 3m),
Must.Be.JsonSerializable<Serializable>(
Has.Property("S").EqualTo("s")
.And.Property("D").EqualTo(3m)));

But imagine you want to check the serialization of your type using a third party library (like the excellent JSON.Net).
Well, nothing prevents the implementation of a IRoundtripSerializer that uses that third party library and the usage of the extension created for such scenario:

var serializer = MockRepository.GenerateStub<IRoundtripSerializer<Serializable>>();
serializer.Stub(s => s.Deserialize()).Return(serializable);
Assert.That(serializable,
Must.Be.Serializable(serializer,
Has.Property("S").EqualTo("s")
.And.Property("D").EqualTo(3m)));

By Example

For the third supported scenario (providing the serialization representation), the abstraction IDeserializer is provided by Testing.Commons:

public interface IDeserializer
{
T Deserialize<T>(string toDeserialize);
}

And the same implementation for usual .NET serialization methods: XmlDeserializer, DataContractDeserializer and JsonDeserializer. Binary serialization is left out as it is pretty useless to provide a string representing the internal binary format. As before, one can use a constraint provided by Testing.Commons.NUnit, DeserializationConstraint instatiable by Must extensions.

Assert.That(Serializable.XmlString("s", 3m),
Must.Be.XmlDeserializable<Serializable>(
Has.Property("S").EqualTo("s")
And.Property("D").EqualTo(3m)));
Assert.That(Serializable.DataContractString("s", 3m),
Must.Be.DataContractDeserializable<Serializable>(
Has.Property("S").EqualTo("s")
.And.Property("D").EqualTo(3m)));
Assert.That(Serializable.JsonString("s", 3m),
Must.Be.JsonDeserializable<Serializable>(
Has.Property("S").EqualTo("s")
.And.Property("D").EqualTo(3m)));

And again, one can go custom implementing the interface and passing the instance to the extension:

var deserialized = new Serializable { D = 3m, S = "s" };

var deserializer = MockRepository.GenerateStub<IDeserializer>();
deserializer.Stub(s => s.Deserialize<Serializable>(serializationRepresentation)).Return(deserialized);
Assert.That(serializationRepresentation,
Must.Be.Deserializable<Serializable>(deserializer,
Has.Property("S").EqualTo("s")
.And.Property("D").EqualTo(3m)));

Using it Already

If you want to be testing the serialization of your types, you can get the latest version of Testing.Commons and Testing.Commons.NUnit from NuGet, which will be, from now one, the only way of getting the binaries (apart from building fro the source).

Daniel Gonzalez Garcia
Vertica A/S