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.

Advertisement

2 Responses to “Unit Testing [Serializable]”

  1. Jason Bock Says:

    This seems like a case where you’d want to use a static analysis rule, like FxCop/CodeAnalysis. This is a design rule. Unit testing this appears to be an odd fit :)

    • Keith Dahlby Says:

      Well it’s a design rule, but it’s also a functional requirement. And we’re not doing static analysis, so this works until we find do. :) Could you point me to an example of how this same kind of verification would be done with static analysis?


Comments are closed.

%d bloggers like this: