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.

Advertisements

Review: Pragmatic Unit Testing In C# with NUnit (2nd Edition)

I’ve written hundreds of tests, read dozens of articles and listened to several presentations on unit testing, but until recently had never actually read a book dedicated to the subject. In reviewing my options, I was told repeatedly that I should start with Pragmatic Unit Testing (In C# with NUnit) from The Pragmatic Programmers, part of the three-volume Pragmatic Starter Kit. In the context of that starter kit, I found the book to be an excellent introduction to unit testing; however, a developer with sufficient experience could probably get by with a quick glance over the summary provided as Appendix C (which is available online).

But before I get into the book, let me start by applauding the idea of the Pragmatic Starter Kit. As I entered industry after receiving my degrees in Computer Engineering and Computer Science, it became clear that I was terribly unprepared for building quality software. Academia provided a solid foundation of theory and some (basic) techniques to structure code (OO, FP, etc), but provided precious little guidance for scaling projects beyond a few thousands lines of code. Version control was given one lecture and a trivial assignment (in CVS), the unit testing lecture did little to convince me that it actually had value, and automated testing was never even mentioned (in fact, build processes in general were scarcely discussed). These are the gaps that the Pragmatic Starter Kit aims to fill with practical advice from the field, and if Pragmatic Unit Testing is any indication the entire series should be required reading for new graduates (or even sophomores, really).

As one would expect from an introductory volume, the book begins with an excellent overview (PDF) of what unit testing is and why it matters. There are also several pages dedicated to rebuttals to common objections like “It takes too much time to write the tests”, “It’s not my job to test my code”, and my personal favorite “I’m being paid to write code, not to write tests”, which is answered brilliantly:

By that same logic, we’re not being paid to spend all day in the debugger either. Presumably we are being paid to write working code, and unit tests are merely a tool toward that end, in the same fashion as an editor, an IDE, or the compiler.

Developers are a proud lot, so the emphasis on testing as a powerful tool rather than a crutch is crucial.

Chapters 2 and 3 follow up with an introduction to testing with NUnit, first with a simple example and then with a closer look at structured testing with the framework. All the usual suspects are covered, including classic and constraint-based asserts, setup and teardown guidance, [Category], [ExpectedException], [Ignore] and more.

The most valuable chapters to a new tester will be chapters 4 and 5. The former provides the “Right BICEP” mnemonic to suggest what to test; the latter takes a closer look at the “CORRECT” boundary conditions (the B in BICEP) to test. The expanded acronyms are included in the aforementioned summary card (PDF). Even after you have a good handle on what to test, the mnemonics can still serve as a handy reminder, and starting out the overviews of each bullet are spot on. I also liked chapters 7–9, which give good guidance on qualities of good tests and how testing can be applied effectively to projects and to improve code, though the refactoring example was a bit longer than it probably needed to be.

In my opinion, the weakest parts of the book were chapters 6 and 10, on mocking and UI testing, respectively. The former starts out strong, but gets bogged down once it starts talking about tools. The reader would be better off skipping section 6.3 altogether in favor of a good Rhino Mocks or Moq introduction. The discussion of UI testing, on the other hand, covers too little on a number of topics to be of much value other than to raise awareness that you should test all parts of the application.

Overall I was quite pleased with the quantity and quality of material covered for an introductory volume, awarding four out of five donkeys. The authors make a good argument for testing and offer sound guidance for how to do it. However, if you’re already familiar with unit testing you may be better off reading The Art of Unit Testing or finding more specific material online.

Posted in .NET, Books. Tags: . 1 Comment »