I have a class that serializes a set of objects (using XML serialization) that I want to unit test.
My problem is it feels like I will be testing the .NET implementation of XML serialization, instead of anything useful. I also have a slight chicken and egg scenario where in order to test the Reader, I will need a file produced by the Writer to do so.
I think the questions (there’s 3 but they all relate) I’m ultimately looking for feedback on are:
- Is it possible to test the Writer, without using the Reader?
- What is the best strategy for testing the reader (XML file? Mocking with record/playback)? Is it the case that all you will really be doing is testing property values of the objects that have been deserialized?
- What is the best strategy for testing the writer!
Background info on Xml serialization
I’m not using a schema, so all XML elements and attributes match the objects’ properties. As there is no schema, tags/attributes which do not match those found in properties of each object, are simply ignored by the XmlSerializer (so the property’s value is null or default). Here is an example
<MyObject Height="300">
<Name>Bob</Name>
<Age>20</Age>
<MyObject>
would map to
public class MyObject
{
public string Name { get;set; }
public int Age { get;set; }
[XmlAttribute]
public int Height { get;set; }
}
and visa versa. If the object changed to the below the XML would still deserialize succesfully, but FirstName would be blank.
public class MyObject
{
public string FirstName { get;set; }
public int Age { get;set; }
[XmlAttribute]
public int Height { get;set; }
}
An invalid XML file would deserialize correctly, therefore the unit test would pass unless you ran assertions on the values of the MyObject.
I would argue that it is essential to unit test serialization if it is vitally important that you can read data between versions. And you must test with “known good” data (i.e. it isn’t sufficient to simply write data in the current version and then read it again).
You mention that you don’t have a schema… why not generate one? Either by hand (it isn’t very hard), or with
xsd.exe. Then you have something to use as a template, and you can verify this just usingXmlReader. I’m doing a lot of work with xml serialization at the moment, and it is a lot easier to update the schema than it is to worry about whether I’m getting the data right.Even
XmlSerializercan get complex; particularly if you involve subclasses ([XmlInclude]), custom serialization (IXmlSerializable), or non-defaultXmlSerializerconstruction (passing additional metadata at runtime to the ctor). Another possibility is creative use of[XmlIngore],[XmlAnyAttribute]or[XmlAnyElement]; for example you might support unexpected data for round-trip (only) in version X, but store it in a known property in version Y.With serialization in general:
The reason is simple: you can break the data! How badly you do this depends on the serializer; for example, with
BinaryFormatter(and I know the question isXmlSerializer), simply changing from:to
could be enough to break serialization, as the field name has changed (and
BinaryFormatterloves fields).There are other occasions when you might accidentally rename the data (even in contract-based serializers such as
XmlSerializer/DataContractSerializer). In such cases you can usually override the wire identifiers (for example[XmlAttribute("name")]etc), but it is important to check this!Ultimately, it comes down to: is it important that you can read old data? It usually is; so don’t just ship it… prove that you can.