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:
- check that the type can be successfully serialized and de-serialized
- check that the serialized form of an object contains the same values as the original
- 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.
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:
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:
By Example
For the third supported scenario (providing the serialization representation), the abstraction IDeserializer
is provided by Testing.Commons:
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.
And again, one can go custom implementing the interface and passing the instance to the extension:
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).