Unit Testing [Serializable]

A common struggle with unit testing is
figuring when to just assume somebody else’s code works. One such example
is serializability: for simple classes, it should “just work” so we
shouldn’t need to write a unit test for each of them. However, I still
wanted to be able to verify that all classes in certain namespaces were
marked as [Serializable], so I wrote the following test:

[TestCase(typeof(Money), "Solutionizing.Domain")]
[TestCase(typeof(App), "Solutionizing.Web.Models")]
public void Types_should_be_Serializable(Type sampleType, string @namespace)
{
var assembly = sampleType.Assembly;

var unserializableTypes = (
from t in assembly.GetTypes()
where t.Namespace != null && t.Namespace.StartsWith(@namespace, StringComparison.Ordinal)
where !t.IsSerializable && ShouldBeSerializable(t)
select t
).ToArray();

unserializableTypes.ShouldBeEmpty();
}

After we have a reference to the Assembly under test, we
use a LINQ to Objects query against its types. If a type matches our
namespace filter, we make sure it’s serializable if it should be.
Finally, by using ToArray() and ShouldBeEmpty()
we’re given a nice error message if the test fails:

TestCase 'Solutionizing.Tests.SerializabilityTests.Types_should_be_Serializable(Solutionizing.Domain.Money, Solutionizing.Domain)'
failed:
Expected: <empty>
But was:  < <Solutionizing.Domain.Oops>, <Solutionizing.Domain.OopsAgain> >
SerializabilityTests.cs(29,0): at Solutionizing.Tests.SerializabilityTests.Types_should_be_Serializable(Type sampleType, String namespace)

I use a few criteria to determine if I expect the type to be
serializable:

private bool ShouldBeSerializable(Type t)
{
if (IsExempt(t))
return false;
if (t.IsAbstract && t.IsSealed) // Static class
return false;
if (t.IsInterface)
return false;
if (!t.IsPublic)
return false;

return true;
}

Other than IsExempt(), the code should be more or less
self-explanatory. If you had never bothered to check how static classes
are represented in IL, now you know: abstract (can’t be instantiated) +
sealed (can’t be inherited). Also, note that !IsPublic will
cover compiler-generated classes for iterators and closures that we
don’t need to serialize.

The final piece is providing a way we can exempt certain classes from
being tested:

private bool IsExempt(Type t)
{
return exemptTypes.Any(e => e.IsAssignableFrom(t));
}

private Type[] exemptTypes = new []
{
typeof(SomeClassWithDictionary), // Wrapped dictionary is not serializable
typeof(Attribute) // Metadata are never serialized
};

Of course, this isn’t a replacement for actually testing that custom
serialization works correctly for more complicated objects, particularly
if your classes may depend on others that aren’t covered by these
tests. But I have still found this test to be a useful first level of
protection.

Related Articles:

About Keith Dahlby

I'm a .NET developer, Git enthusiast and language geek from Cedar Rapids, IA. I work as a software guru at J&P Cycles and studied Human-Computer Interaction at Iowa State University.
This entry was posted in NUnit, Serializable, testing. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.mohundro.com/blog/ David Mohundro

    I have occasionally written tests to ensure that my objects serialize correctly to sort of “future proof” the classes. For example, I might want to ensure that someone doesn’t add a non serializable type to it at some point in the future.

    It certainly doesn’t make sense to do this in all cases, but it has saved me in the past :)

  • http://interfacingreality.blogspot.com/ Yann Trevin

    Interestingly enough, Gallio/MbUnit has a set of built-in assertions which exactly do that: Assert.IsSerializableType, Assert, SerializeThenDeserialize, etc.

  • http://www.mrkwatkins.co.uk/Blog Kevin Watkins

    I’ve done something similar before using NUnit. I used the ValueSourceAttribute on my test method to specify another method that generates the list of types to check. Then the test method itself takes each type in turn and does Assert.That(type, Is.Serializable) to check that it’s serializable.

    The advantage of this approach is that you get a separate test for each type being checked. Therefore if one isn’t serializable you can see at a glance which one it is. Looks like with the approach above you’ve have to debug the test first to find out which type(s) aren’t serializable.