Thursday, March 6, 2014

Code Contracts and Test-Driven Development

Introduction

This post will be mostly about Microsoft’s Research in Software Engineering (RiSE) group’s Code Contracts. I’ll also cover the role of code contracts in conjunction with test-driven development. There seems to be a debate over the role of test-driven development when using code contracts. I’m going to weigh in on that debate.

In order to demonstrate code contracts, I’m going to return to the Category class that I used as an example in my blog post Advanced Test-Driven Development. This post may also be a little on the longer side.

An Introduction to Code Contracts

What are code contracts? Where did they come from? Why should I care?

The term ‘code contract’ was coined by Bertrand Meyer while he was developing the Eiffel programming language around 1986. In fact, code contracts are a core part of the Eiffel programming language to this day.

Code contracts are a way of specifying pre-conditions, post-conditions for methods and invariants for objects. Pre-conditions are predicates that specify conditions that must be met in order for a method to be in a state that can result in successful execution. Post-conditions are predicates that must be met upon any exit from a method. Invariants are predicates that validate state that must hold at all throughout the lifetime of an object.

Microsoft & Code Contracts

Microsoft’s RiSE team first introduced code contracts to .NET as an extension to the C# language known as Spec#. Spec# was basically a DSL especially for defining code contracts when programming in C#. Fast forward a few years, and shortly after the release of the .NET Framework 3.5, the RiSE team released the successor to Spec#, Code Contracts, in the form of an assembly that you can reference from your project. This assembly replaced the DSL with a native C# language implementation. For the first time in .NET (aside from the Spec# augmented language), programmers could employ code contracts (and not just simple guard conditions at method entry and rudimentary forms of post-conditions). With the release of the .NET Framework 4.0, Code Contracts have found their way into the base class library (BCL) itself. Code contracts can be found in the System.Diagnostics.Contracts namespace. You no longer need to reference an external assembly to employ code contracts.

In order to make full use of code contracts in your .NET projects, you will need to install the necessary tooling from Microsoft’s RiSE Code Contracts site. Installing this tooling installs a new project properties tab for controlling how contracts are incorporated into your assemblies, whether contracts will be enforced at runtime, and/or whether or not the static analyzer will perform an analysis of your code. For the full details, check out the Code Contracts documentation. Furthermore, the tooling installs a code contracts re-writer. The re-writer is the component responsible for weaving the necessary IL into your assemblies in order to enforce the contracts according to the options specified. OK, enough of the history lesson. Let’s get to the fun part: the code.

Using Code Contracts with the Category Class

As I stated in the introduction to this post, I’m going to be reusing the Category class example I went through in my post on advanced TDD. This class represents a category that you might find in some online e-commerce product catalog. I'm going to highlight just a few areas of the Category class as it pertains to implementing code contracts. The first part of the class we’re going to look at is instantiating the class given a name, since the pre- and post-conditions are pretty easy to follow. Also, because of this easiness, it will be easier to introduce how to incorporate unit testing when using Code Contracts.

So, starting with the a category's name: it cannot be null; it must not be the empty string; and it must not consist solely of whitespace. In my advanced TDD post, we enforced these requirements with the following code:

public class Category
{
    private string name;

    public Category(string name)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new InvalidCategoryNameException();

        this.name = SanitizeName(name);
    }
}

The guard clause is known as a pre-condition. This particular form of pre-condition is considered a defensive programming style. If the name parameter is null, the empty string, or only whitespace, an InvalidCategoryNameException exception is thrown. Notice that the constructor has no post-conditions—there is no code to ensure that the private class field name also satisfies these conditions. (In all honesty, it's hard to think of a reason why, if the name parameter meets the pre-conditions, the corresponding class field would not end up being assigned.) Let's see what this same constructor would look like using Code Contracts.

using System;
using System.Diagnostics.CodeContracts;

namespace DDDSportsStore.Domain
{
    public class Category
    {
        private string name;

        public Category(string name)
        {
            Contract.Requires<InvalidCategoryNameException>(
                !string.IsNullOrWhiteSpace(name)
            );
            Contract.Ensures(!string.IsNullOrWhiteSpace(this.name));

            this.name = SanitizeName(name);
        }
    }
}

As you can see, the guard clause was replaced with a call to Contract.Requires<T>(bool precondition). (Important: read the documentation before using the generic form of the Requires method in your release code.) This line of code guarantees that if the name parameter does not meet the specified pre-condition, the specified exception will be thrown. Similarly, we have stated a post-condition: namely that when this constructor exits, the class's name field will also not be null, an empty string, or whitespace. In my advanced TDD post, the constructor ended up having a lot more guard clauses in it for other properties of the Category class. Don't worry, we'll come to those soon.

Code Contract Violations

When a pre-condition, post-condition, or invariant condition is violated, an exception is thrown. The exception is the System.Diagnostics.Contracts.ContractException. The catch is, this exception’s access modifier is internal; so you can’t catch this exception directly. This is actually a very good design decision on the part of the RiSE team, even thought it causes headaches for us developers—especially when it comes to unit testing. Programmers should not be able to circumvent contract violations by catching these exceptions and “handling” them. Contracts are guarantees. Pre-conditions guarantee that, given parameters that satisfy the stated pre-conditions, the method can perform its duties. Similarly, post-conditions guarantee to the caller that upon exit from the method, certain conditions will be true. And invariants guarantee that certain conditions hold throughout the lifetime of the object before and after each method call. So while this is a bit of a pain when it comes to unit testing, this was a very good decision.

Code Contracts and Test-Driven Development

As I alluded to in the introduction to this post, there is a debate that’s been going on in Internet land regarding the roles of code contracts and test-driven development and when and how they are used together. I’ve seen many people giving bad advice in regards to these development practices and I wish to argue the case for my opinion in the next few paragraphs.

What, traditionally, has been the purpose of unit tests (especially those unit tests written as part of a test-driven development effort)? Traditionally, they’re written to ensure that a method does what it’s supposed to do. In fact, there’s a whole style of test-driven development known as behavior driven development (BDD). BDD’s main focus is tests that are named after the behavior that the function is performing. BDD tests strive to be as human readable as possible in an effort to be somewhat of a requirements document for the code base. (I’m over simplifying a bit—I urge you to become familiar with it, as the style has many merits over regular TDD.) But no matter the testing style you use, tests are written to verify that: 1) method parameters are checked to ensure successful execution of a method; 2) the method executes successfully and the proper outputs are returned; and 3) that the state of the object is valid when the method has finished executing (not all tests require this last point to be checked). The first point sounds a lot like pre-conditions—and in a sense, that’s exactly what’s being checked. The second point sounds exactly like post-conditions—and it is. The third point, if part of the test, might be an object invariant (I say might, because what’s being tested in the third point may only be true for that particular case and not a true invariant.)

So now the question is, if I use code contracts, do I need to test pre-conditions and post-conditions? Emphatically, I say YES!! You will find many who disagree with me. Let’s examine the two main arguments against writing tests that exercise code contracts.

You Don’t Write Unit Tests that Exercise Debug Asserts—Why Write Tests That Exercise Your Contracts?

First, I would like to say that this is an invalid statement to begin with. Of course you don’t exercise debug asserts. There’s nothing you can do with them in the testing framework. Your test will fail if a debug assert fails because the program aborts (and therefore, your test runner aborts, too)! So that’s just a ridiculous comparison to make. Furthermore, pre-conditions are not debug asserts. I think this argument comes about because there is a Contract.Assert method in Code Contracts. But this method is not used as a pre- or post-condition. The Contract.Assert method is used as a hint to the static analyzer that, at some point in your code, some condition should hold. That’s it. It’s just a hint to the static analyzer—not a contract. These hints are sometimes needed in order to give the static analyzer a little bit of help when it can’t quite figure out what should hold or not.

Testing Code Contracts Pre-Conditions is Akin to Testing the BCL

This argument is almost plausible; but ultimately, it is flawed. First, it is true that Code Contracts are now a part of the .NET base class library. That’s what makes this argument so plausible. However, when you write tests to test your pre-conditions, the goal is not to test that Code Contracts “does the right thing” when a contract is violated. Indeed, that would be silly. It’s 3rd party code that I have no control over, so why test it? No, what I am testing is that I didn’t accidentally mistype the condition that is checked as part of the pre-condition contract!

Seriously, you must write unit tests for your pre-conditions. While adding code contracts to the Category class used in this blog post, I did mistype a pre-condition specification. The specification was supposed to ensure that when adding a subcategory to a category, the category being added as a subcategory is not null and was not the NullCategory. So when I ran my unit tests and two of them failed (again, I had added the contracts after I had already written the majority of the class in this instance using the standard defensive coding practice of guard clauses at method entry), I was surprised. What happened is that I had accidentally used an || instead of an && in the pre-condition (I probably had copied the guard clause condition; pre-conditions are often the opposite of traditional guard clauses—guard clauses check that something isn’t true, pre-conditions check that something is true). So again I say, YES, you must test your pre-conditions to ensure, not that Code Contracts is behaving, but that you didn’t mistype your pre-condition!

I do find, however, that it’s not practical to test your post-conditions, even though the same possibility of mistyping the post-condition exists. This is because 1) it’s really hard to make your method fail a post-condition on purpose, and 2) chances are, if your post-condition is wrong, a ContractException exception will be thrown. Again, this argument is not water tight—but it is difficult to test post-conditions, and therefore I believe, not worth the effort.

I think that these two arguments are the most frequent reasons given for not testing your code contracts; and I think I have demonstrated why these arguments don’t hold water.

Test-Driven Development with Code Contracts

Now that we have that out of the way, I want to share with you how you can perform test-driven development while using code contracts. This did take me quite a bit of time to figure out. So let’s re-introduce unit tests for the constructor as it's currently implemented above.

The first thing we need to take care of is the fact that when a contract is violated, a private ContractException exception is thrown, and since it’s private, it cannot be caught. So how can we test our pre-conditions? Code contracts provides an event on the static Contract class called ContractFailed. You can subscribe to this event to get access to the contract exception that was thrown. You can also flag the exception as handled from within this event. (So apparently, even thought you can’t catch the exception, you can subscribe to this event and effectively handle it there. Why any good developer would do this is beyond me, after all, why use code contracts in the first place, then? But for unit testing, this is just the hook we need for testing our contracts—but this is the only place where you should ever write this code.)

Now if you remember back to our constructor example, I was using the generic form of the Contract.Requires method that allowed me to specify the exception that is thrown. How this works is, upon a contract violation, code contracts will still throw the System.Diagnostics.Contracts.ContractException exception. But there is a class that is emitted as part of the code contracts re-writer that is able to catch this exception. When the exception is caught, the exception’s InnerException property contains an instance of the exception specified for the generic type parameter. This inner exception is then thrown back to the application. (Read the documentation before using the generic version of Contract.Requires.)

Shown below, I have written a class that subscribes to the Contract.ContractFailed event,  which is meant to serve as a base class for all test classes that will be testing objects written with code contracts.

using System;
using System.Diagnostics.Contracts;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace DDDSportsStore.Domain.Tests
{
    // Probably not the best named class for this purpose...
    // but good enough.
    [TestClass]
    public abstract class UnitTestBase
    {
        private EventHandler<ContractFailedEventArgs> ContractFailedHandler = 
            new EventHandler<ContractFailedEventArgs>((s, e) => e.SetHandled());

        [TestInitialize]
        public void HookContractFailed()
        {
            Contract.ContractFailed += ContractFailedHandler;
        }

        [TestCleanup]
        public void UnhookContractFailed()
        {
            Contract.ContractFailed -= ContractFailedHandler;
        }
    }
}

This class has both a setup and a teardown method. It hooks and unhooks an event handler to handle the Contract.ContractFailed event. In the official documentation, the RiSE team show that you should call e.SetUnwind(). If you call that method, your unit tests will stop running the moment a contract is violated. That’s because that method causes another exception to be thrown. Any exception thrown from the ContractFailed event handler winds up causing the application to terminate—which means no other tests will run because the test runner will be terminated. But in this case, we want the exception to be thrown because it verifies that when we pass invalid parameters to the method under test, that the pre-condition contract was specified correctly, and therefore, the correct exception is thrown. So now you can use your test framework’s method for asserting thrown exceptions and the tests will pass. Let’s look at an example test that tests our Category constructor by passing a null name, an empty string name, and a name consisting entirely of whitespace.

[TestClass]
public CategoryTests : UnitTestBase
{
    private void AssertCannotCreateCategoryWithName(string name)
    {
        ExceptionAssert.Throws<InvalidCategoryNameException>(
            () => new Category(name)
        );
    }

    [TestMethod]
    When_creating_a_category_with_an_invalid_name_then_an_InvalidCategoryNameException_is_thrown()
    {
        AssertCannotCreateCategoryWithName(null);
        AssertCannotCreateCategoryWithName(string.Empty);
        AssertCannotCreateCategoryWithName("\n");
    }
}

If you look back to my previous post, you will see my implementation of the ExceptionAssert class that I created to handle testing for thrown exceptions. I prefer using this utility class to using the ExpectedExceptionAttribute attribute. Pretty much, that's all there is to incorporating unit tests with code that uses code contracts and maintaining the ability to test that you typed your pre-conditions correctly. If you don't want to use the generic form of the Contract.Requires method (and there may be good reasons for you to do so, see the code contracts documentation for details), then you may need to modify how you test your pre-conditions. But the basic elements remain the same. I'll leave that as an exercise for you.

For the remainder of this blog post, I'm going to skip showing unit tests. As, apart from what I showed above, there's nothing different about my unit tests than what I demonstrated in my previous blog post. For the rest of this post, I want to focus on Code Contracts and incorporating more of its features into your classes.

Code Contracts and Inheritance

The great thing about Code Contracts is that you don’t need to redefine them on all of your sub-types. Once you define them on a base class, they’re defined for all sub-types. In this way, using Code Contracts can help ensure that your code follows the Liskov Substitution Principle (LSP). Because code contracts are inherited, all derived types can be used in place of their base types and clients are guaranteed that all pre-conditions, post-conditions, and invariants of the base type will still hold on all derived types.

This does have some implications for derived types however. Derived types are not allowed to define pre-conditions that are stronger than their base types (in fact, doing so would most likely be a direct violation of the LSP to begin with). It is interesting to note, that while weaker pre-conditions should be allowed, they are not. The RiSE team, in their work with Spec#, found that allowing weaker pre-conditions often provided little value but added a large amount of complexity to the analyzer. So in effect, you cannot even add any pre-conditions to derived types. This is even true for interfaces (after all, interfaces are nothing more than pure, abstract classes for which C# has simply provided syntactic sugar (and built-in compiler constraints) in the form of the interface keyword). It should also be noted that if you don’t specify any pre-condition on a method in a base class, then that method is considered to have the weakest of all pre-conditions: true.

These same rules, however, do not apply to post-conditions. You are allowed to specify stronger post-conditions on derived types. This just provides further guarantees for clients that call those methods on derived types compared to their base class method counterparts.

Code Contracts and Abstract Class Methods and Interfaces

Naturally, the question should arise: how can I implement contracts on abstract class methods and interfaces, neither of which are permitted to have method bodies? The answer is to create a special class that overrides the abstract class methods or implements the interface. Code Contracts provides two attributes, one that adorns the abstract class or interface (ContractClassAttribute), and the other that adorns the contract class itself (ContractClassForAttribute).

Keeping Your Contracts DRY

If you find yourself repeating many contracts over and over again in various methods throughout your objects, you can group them together into methods adorned with the ContractAbbreviatorAttribute attribute and call them directly from methods that require the contracts. Let me show you an example. For any Category that is created, it must have a non-null list of subcategories. It’s parent category must also not be null (if a category has no parent, it should have the NullCategory Category.None assigned as its property value). And of course, a category should not have a null, empty string, or whitespace only name after it’s been instantiated. Here’s how I can “group” my contracts together:

public class Category
{
    public static readonly Category None = new NullCategory();

    private string name;
    private Category parentCategory;
    private List<Category> subCategories;

    protected Category()
    {
        subCategories = new List<Category>();
    }

    public Category(string name) : this(name, None) { }

    public Category(string name, Category parent) : this()
    {
        Contract.Requires<InvalidCategoryNameException>(
            !string.IsNullOrWhiteSpace(name)
        );
        Contract.Requires(parent != null);
        EnsureThisCategoryIsValid();

        this.name = SanitizeName(name);
        if (parentCategory == null)
            parentCategory = parent;
        else
            SetParentCategory(parent);
    }

    [ContractAbbreviator]
    private void EnsureThisCategoryIsValid()
    {
        // Obviously, separate calls to Ensures or Requires are ANDed together...
        Contract.Ensures(subCategories != null);
        Contract.Ensures(
            this == None || 
            !(string.IsNullOrWhiteSpace(name) || parentCategory == null)
        );
    }
}

Again, while this example is trivial, if you have a lot of methods with the same pre- and/or post-conditions, this can be a valuable tool in keeping your code contracts code DRY.

Specifying Object Invariants

Code contracts has a way of telling the analyzer the various invariants of your objects. Invariants are conditions that must be met by your object after each public method call. In the Category class, for example, the following invariants are true: a category’s name is not null, a category’s parent is not null, and a category’s list of subcategories is not null. These conditions can be checked by placing them within calls to Contract.Invariant inside of a method adorned with the ContractInvariantMethodAttribute attribute as shown below:

[ContractInvariantMethod]
public void CategoryInvariants()
{
    Contract.Invariant(Name != null);
    Contract.Invariant(Parent != null);
    Contract.Invariant(Subcategories != null);
}

Invariants can be especially useful with automatic properties (properties that don't have explicit backing fields that are retrieved/assigned from the property's getters/setters).

Code Contract Helper Methods

Let's say you have some helper method to help you in determining whether certain conditions hold (either for pre- or post-conditions on a method) due to some complex logic. You don't really want all that complex logic in the Contract.Requires or Contract.Ensures method call. So instead, you'll have something like Contract.Ensures(ThisMethodsResultsAreVaild());. In order to use this method without code contracts emitting an error at compile time, you should mark the method as pure using the PureAttribute attribute. This tells code contracts that this method has no visible side-effects and will remove the compiler error. The effects of the PureAttribute attribute are also inherited by derived classes.

Giving the Analyzer a Helping Hand

Determining the various states that the source code could leave the object in at any particular time is a fairly difficult task. You’ll see this is true when you start to use Code Contracts because it will slow down your build times a bit (static analysis runs as a post-build activity). For this reason, Microsoft does suggest that you only run contract checking for a custom build configuration that you would create in addition to your normal Debug and Release builds (e.g., Checked).

Due to the complexity involved in analyzing the source code, sometimes the static analyzer will ask for your help. It does this by writing informational messages to the Error Output window in Visual Studio. Sometimes it tells you exactly what it’s looking for in the form of suggesting appropriate Contract.Requires or Contract.Ensures statements. Other times, it tells you that it’s assuming one thing or another (and suggests that you specify these assumptions for next time).

Code Contracts has two other static methods on the Contract class called Assume and Assert. These are not pre- or post-conditions or even object invariants. In the case of Assume, you’re informing the static analyzer that it should assume the condition specified for the argument to the method is true at that point in the execution of the code. It is merely a hint to the analyzer. On the other hand, Assert tells the static analyzer that some condition must be true at that point in code execution in order for successful execution to continue. If at runtime (for builds where asserts are left in the code, such as your Checked build confiuration) an assertion is violated, your program will terminate.

Conclusion

I've touched upon many of the features of Microsoft's RiSE Code Contracts in this blog post along with a few code illustrations. If you find Code Contracts interesting and an acceptable way of practicing Design by Contract, I encourage you to check out the Code Contracts website and the corresponding documentation. Below, find the complete Category class re-written to include all Code Contracts. Except for having to hook the ContractFailed event in a base class for all my unit tests in order to test that expected exceptions where thrown, the unit tests did not need to change in any other way, and so I have not repeated them here. Until next time, take care.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Text.RegularExpressions;

namespace DDDSportsStore.Domain
{
    [DebuggerDisplay("[Name: {Name}]")]
    public partial class Category
    {
        public static readonly Category None = new NullCategory();
        private string name;
        private Category parentCategory;
        private List subCategories;

        protected Category() 
        {
            subCategories = new List();
        }

        public Category(string name) : this(name, None) { }

        private Category(string name, Category parent) : this()
        {
            Contract.Requires<InvalidCategoryNameException>(
                !string.IsNullOrWhiteSpace(name)
            );
            RequiresCategoryIsNotNull(parent);
            EnsureThisCategoryIsValid();

            this.name = SanitizeName(name);

            if (parentCategory == null)
                parentCategory = parent;
            else
                SetParentCategory(parent);
        }

        protected virtual int Id { get; set; }

        public virtual string Name
        {
            get { return name; }
            set 
            {
                RequiresValidName(value);
                name = SanitizeName(value); 
            }
        }

        public virtual Category Parent 
        {
            get { return parentCategory; }
            set 
            {
                RequiresCategoryIsNotNull(value);
                Contract.Assume(parentCategory != null);
                SetParentCategory(value); 
            }
        }

        public IReadOnlyCollection Subcategories 
        {
            get 
            {
                Contract.Ensures(
                    Contract.ForAll(
                        Contract.Result<IReadOnlyCollection<Category>>(), 
                        c => c != null && c != Category.None
                    )
                );
                Contract.Assume(subCategories != null);

                return subCategories; 
            } 
        }

        /// <summary>
        ///     Creates a new <see cref="Category"/> as a subcategory of the
        ///     current instance.
        /// </summary>
        /// <param name="name">The name of the new category.</param>
        /// <exception cref="InvalidOperationException">
        ///     Thrown if the current instance is <see cref="Category.None"/>
        /// </exception>
        /// <exception cref="InvalidCategoryNameException">
        ///     Thrown if the <paramref name="name"/> is <c>null</c>, 
        ///     the empty string, or consists entirely of whitespace.
        /// </exception>
        public virtual Category AddSubcategory(string name)
        {
            RequiresValidName(name);

            Category subCategory = new Category(name, this);
            AddSubcategory(subCategory);
            return subCategory;
        }

        private void AddSubcategory(Category c)
        {
            Contract.Requires<InvalidOperationException>(
                this != Category.None && !(c == None || c == null)
            );
            Contract.Requires<InvalidOperationException>(
                !(
                      Contract.Exists<Category>(
                          subCategories, sc => sc.Name == c.Name
                      )
                 )
            );

            this.subCategories.Add(c);
        }

        public bool Equals(Category other)
        {
            if ((object)other == null)
                return false;

            return this.ToString() == other.ToString();
        }

        private string SanitizeName(string proposedName)
        {
            Contract.Assume(!string.IsNullOrWhiteSpace(proposedName));
            return Regex.Replace(proposedName, @"\s+", " ");
        }

        private void SetParentCategory(Category c)
        {
            RequiresCategoryIsNotNull(c);
            RequiresCategoryIsNotNull(parentCategory);
            Contract.Requires<InvalidOperationException>(
                c == Category.None || !this.HasDescendant(c), 
                "Attempted to set the parent of this category to " +
                "one of this category's descendants."
            );

            parentCategory = c;
            if (parentCategory != None)
                parentCategory.AddSubcategory(this);
        }

        #region System.Object Overrides

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            Category c = obj as Category;
            if ((object)c == null)
                return false;

            return this.ToString() == c.ToString();
        }

        public override int GetHashCode()
        {
            return this.ToString().GetHashCode();
        }

        public override string ToString()
        {
            return Parent == Category.None ?
                Name : string.Concat(Parent.ToString(), "/", Name);
        }

        #endregion

        #region Operator Overrides

        public static bool operator ==(Category a, Category b)
        {
            if (object.ReferenceEquals(a, b))
                return true;

            if ((object)a == null || (object)b == null)
                return false;

            return a.ToString() == b.ToString();
        }

        public static bool operator !=(Category a, Category b)
        {
            return !(a == b);
        }

        #endregion

        #region Code Contracts Helper Methods

        [ContractInvariantMethod]
        private void CategoryInvariants()
        {
            Contract.Invariant(Name != null);
            Contract.Invariant(Parent != null);
            Contract.Invariant(Subcategories != null);
        }

        [Pure]
        private bool HasDescendant(Category c)
        {
            RequiresCategoryIsNotNull(c);

            bool result = false;

            if (this == c)
                result = true;
            else if (c.Parent != None)
                result = this.HasDescendant(c.Parent);

            return result;
        }

        [ContractAbbreviator]
        private void EnsureThisCategoryIsValid()
        {
            Contract.Ensures(subCategories != null);
            Contract.Ensures(this == None || 
               !(string.IsNullOrWhiteSpace(name) || parentCategory == null));
        }

        [ContractAbbreviator]
        private void RequiresValidName(string name)
        {
            Contract.Requires<InvalidCategoryNameException>(
                this == None || !string.IsNullOrWhiteSpace(name)
            );
        }

        [ContractAbbreviator]
        private void RequiresCategoryIsNotNull(Category c)
        {
            Contract.Requires(c != null);
        }

        #endregion
    }
}

Saturday, March 1, 2014

Mighty Moose and Contextual (a/k/a Hierarchical) TDD

Introduction

In my last few blog posts, I introduced Mighty Moose and advanced TDD using nested, hierarchical context classes. If you have started using Mighty Moose and tried your hand at contextual TDD (that’s my new name for it, it seems to fit), also known as hierarchical testing, you may have noticed a problem.

The Test Runner Behind Mighty Moose

Mighty Moose is compatible with the following testing frameworks:

  • MS Test
  • NUnit
  • xUnit
  • MbUnit
  • SimpleTest
  • MSpec

What may be surprising is that Mighty Moose does not necessarily use the native test runners for these frameworks. Mighty Moose has its own test runner called AutoTest.Net. From what I can tell, it appears that Mighty Moose implements a bunch of Adapters that it uses to interact with the various testing frameworks it supports. The Adapters don’t make use of the native test framework engines themselves. The Adapters contain their own implementation. Unfortunately, it appears the execution of the Adapters may not be 100% compatible with the test framework you’re using to write your tests.

So, imagine my surprise when I refactored all of my tests to use nested, hierarchical classes in order to constrain the boundaries of the various setups I would need (see my last blog post on advanced TDD). Mighty Moose started reporting that all of my abstract base class tests were broken! (The actual error was that AutoTest.Net was unable to instantiate an abstract class. No, really?) I ran my tests in Visual Studio using the CTRL+R, T shortcut. This invokes the native MS Test test runner. I pop on over to the Test Explorer window, and what do I find? All of my tests pass.

On the one hand, having these Adapters are great; you don’t have to learn a different testing framework API in order to use Mighty Moose. On the other hand, my confidence in Mighty Moose is now lower than I would otherwise like because the output is not 100% compatible with the native test runner. How can I be sure that in all instances the tests are actually passing or failing (or even that AutoTest.Net is reporting the right result, for that matter)?

AutoTest.Net is Broken

So obviously, there’s a bug in AutoTest.Net, which is what Mighty Moose uses to run your tests; and it’s a pretty big one. AutoTest.Net recognizes that you have various test classes (a là the TestClassAttribute attribute). And so it tries to instantiate any class decorated with that attribute without checking if it’s valid to instantiate said class. But wait, it gets worse. It finds the nested inner class (which derives from the abstract base classes) and only runs the tests found in the concrete derived class (none of the inherited base class tests run with the derived class).

Is there a way around this? Well, yes, but you should be careful. The way to avoid this problem with Mighty Moose is to not make the base classes abstract. However, this will lead to the base class tests executing for every class which derives from that base class. And this won’t be true just for AutoTest.Net, but also for MS Test. (I don’t know about NUnit and xUnit since I’ve never used them.)

So what, you say? That’s ok, as long as Mighty Moose works. Well, consider this. You have 100 tests in your base class, and 100 tests in a derived class. How many tests will get executed? 300 tests. 100 for the base class, 100 for the derived class’s inherited base class methods, and 100 tests contained in the derived class itself. OK, so you don’t have 100 tests in a single test class. However, in a production system, you may have upwards of 5000 tests. (And that’s just when they’re running once!) So now multiply a good chunk of those tests and pretty soon you’re pushing 15000 tests. (And keep in mind that that's assuming that you're only nesting one level. If you nest two levels deep, now you have an explosion of the possible number of tests that will execute.) It’s wasteful of time and computing resources.

Conclusion

If you have a really small project and don’t mind tests being executed more than once, by all means, go for it. But it’s really not a good of a solution. AutoTest.Net should take into account whether or not a class is abstract and ignore it if it’s marked with the TestClassAttribute attribute. Furthermore, AutoTest.Net should look to see if a test class is a derived class and ensure that the derived class’s base class hierarchy is properly instantiated and that all inherited members are executed as part of the derived class.

Hopefully the authors of AutoTest.Net will fix this bug soon. Until then, if you have a small project, go for it. Otherwise, it’s probably best not to use Mighty Moose. If you really need a continuous test runner, check out NCrunch. I’ve been using it at work and I really like that continuous test runner, too. (Oh, and as far as the cost for NCrunch, while I don’t want to pay for it for personal use at this time, it’s really not that expensive; and if things were different as far as the level of development I do personally, I’d definitely pay for it.)

Tuesday, February 25, 2014

Advanced Test-Driven Development

Introduction

At my current place of employment, I was recently introduced to the “Uncle Bob” series of videos, more properly titled Clean Coders. In episodes 19 through 23, Robert Martin begins a series on advanced test-driven development.

One of the things that was mentioned in that series is about how your tests should be treated no differently than production code. That means that your tests should also follow the SOLID principles: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation/Separation, and the Dependency Inversion principles (see the videos for clarifications surrounding applying these principles in relation to unit tests, however). Also, another good coding principle is DRY—don’t repeat yourself.

Traditional TDD

In .NET, I use the standard MS Test test framework. I know, it’s not great, and I should use NUnit or xUnit, right? But really, I don’t like NUnit (or xUnit). Actually, MS Test isn’t great either (too many attributes), but I like it better than NUnit or xUnit. Feel free to disagree with me. I'm not writing this blog post to argue about the best testing framework. See Roy Osherove's blog for a more in-depth discussion regarding the merits (or lack thereof) of various testing frameworks. (Hint: he has a very low opinion of MS Test—but that's OK, he's entitled to his opinion.)

Anyway, the point is, the MS Test framework does not support the notion of setup contexts (nor does NUnit or xUnit, for that matter). Yes, yes, there is a TestInitializeAttribute that you can adorn a method with to provide a test class with a setup method (and NUnit’s corresponding SetupAttribute attribute), but that’s not what I’m talking about.

Usually when we create tests, we create one test class for each class under test (CUT). Invariably, one of two things can take place when it comes to common data needed for tests (though, maybe not needed for each test):

  1. Create private test class fields and initialize them in the setup method adorned with the TestInitializeAttribute attribute
  2. Just re-create all test setup information within each test method in the Arrange section of the test


I usually follow number 2, but I was always bothered by it. After all, it violates DRY. I just hate it. But option 1 is no better! You end up instantiating a lot of fields and data for each and every test method in the class, regardless of whether the currently executing test needs all of the data. This can be terribly inefficient, especially if your tests involve creating expensive objects.

So how can we get small setup methods and small tests?

Advanced TDD with Context Classes

The solution to this problem is recognizing that there are various contexts under which your tests are running. Some contexts may be completely separate from others, while other contexts need to use data that also appears in a different context. The real trick is determining how to arrange the contexts so that you don’t violate the DRY principle and are able to promote re-use in order to keep setup methods small, all the while keeping your tests really small. I got the idea for this concept directly from the Clean Coders video series. (Please, if you hate this idea, don’t misconstrue the fact that because I got this idea from the Clean Coders video series that the series must be junk. It’s not. I just might have a bad idea here. I don’t think so; but hey, I’m entitled to my opinion. :) Of course, I’m always interested in feedback and open discussion.)

I first tried this concept out on some code at my place of employment. But of course, I can’t show you that code. So, the rest of this article is going to demonstrate this concept using a class called Category. While this class is really simple, and you may disagree with my implementation, this is not about the Category class implementation. It’s about how you can structure your tests to keep them SOLID, DRY, and focused.

It should be noted that the method outlined here may not work on Visual Studio 2010. It will work with Visual Studio 2012 and 2013, however. If anyone finds that the methods outlined here work on Visual Studio 2010, too, I’ll update this post accordingly.

Introducing the Category Class

In order to demonstrate the concept, I’m going to build up the Category class incrementally, practicing TDD as I go along. So, without further delay, let’s get started.

First, I want to outline some invariants of the Category class:

  • A category name cannot be null or the empty string and it cannot be a whitespace only string. If any of these conditions are true, throw an InvalidCategoryNameException exception.
  • A category can have a single parent. The parent cannot be null—the parent property must always return an object.
  • A category can have sub-categories. You cannot add a null sub-category. Adding a sub-category must not result in a cycle, e.g. A -> B -> C -> A, where the first and last category A are the same exact category object.


As I go along, I’ll introduce other business rules and/or requirements. So, what test should I write first? Well, I think I'll write a test to make sure that I can create a Category.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace DDDSportsStore.Domain.Tests
{
    [TestClass]
    public class CategoryTests
    {
        [TestMethod]
        public void Can_create_a_category()
        {
            Category c = new Category();
        }
    }
}

Now, of course this test won’t compile because I haven't yet written the Category class. So I'm going to go ahead and create that now.

using System;
using System.Collections.Generic;
using System.Linq;

namespace DDDSportsStore.Domain
{
    public class Category
    {
    
    }
}

There, that's all that's needed in order to make the test above pass. If you haven’t guessed, this category class is implementing a product category that you might find as part of an online e-commerce catalog. I’m trying to practice domain-driven design (DDD) with an example used in the Pro ASP.NET MVC4 book published by APress. Enough about DDD; that’s another story and another blog post (or 10).

Now that I have a passing test and I can indeed create a category object, I'm going to get rid of that first test and replace it with a test that ensures that I cannot create a category with a null name. It would be really hard in a blog format to show the incremental building up of the category class in order to satisfy the test (see episode 24 of Clean Coders on the Transformation Priority Premise), so I'm just going to show you the final implementation of this particular constructor that will pass the test.

[TestMethod]
[ExpectedException(typeof(InvalidCategoryNameException))]
public void Cannot_create_a_Category_with_a_null_string_for_a_name()
{
    Category c = new Category(null);
}

Pay no attention to the ExpectedExceptionAttribute attribute. I'll be replacing that soon enough. Just know that that attribute is a real code smell and could be causing your tests to pass when they shouldn't be. OK, with that said, of course this test should also fail because I have no constructor that takes a parameter. I'm going to implement that constructor now.

public class Category
{
    private string name;

    public Category(string name)
    {
        if (name == null)
            throw new InvalidCategoryNameException();

        Name = name;
    }

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}

Ok, the next part of the first invariant is that the string cannot be empty. I'll write another test to test this invariant.

[TestMethod]
[ExpectedException(typeof(InvalidCategoryNameException))]
public void Cannot_create_a_Category_with_an_empty_string_for_a_name()
{
    Category c = new Category(string.Empty);
}

Again of course, this test fails because in the constructor, I only checked to see if name is null. So now I'll update the constructor to make this test pass.

public Category(string name)
{
    if (string.IsNullOrEmpty(name))
        throw new InvalidCategoryNameException();

    Name = name;
}

There, that should be enough to make the above test pass. The final invariant regarding a category's name is that it cannot consist entirely of white space. The test for this invariant will closely resemble the other two tests. So I think it's time to refactor these tests to remove code duplication and use composable assertions (again, see the Clean Coders videos). The first thing you'll notice about the tests is that I attempt to assign the newly created category to a variable named c. This isn't strictly necessary—I merely need to call the constructor with an invalid category name and the execption should be thrown. So I'll eliminate that duplication in this refactoring. I'll also create a helper method to carry out the assertion. Finally, I'm testing 3 components of a single logical invariant concerning the name of a category. So I could really check all 3 components in a single test and I would not be violating the "single assertion per test" rule. The new tests are shown below.

[TestClass]
public class CategoryTests
{
    [TestMethod]
    public void Cannot_create_a_Category_with_an_invalid_name()
    {
        AssertCannotCreateCategoryWithName(null);
        AssertCannotCreateCategoryWithName(string.Empty);
        AssertCannotCreateCategoryWithName("\n");
    }

    public void AssertCannotCreateCategoryWithName(string name)
    {
        ExceptionAssert.Throws<InvalidCategoryNameException>(
            () => new Category(name)
        );
    }
}

public static class ExceptionAssert
{
    public static void Throws<T>(Action action) where T : Exception
    {
        try
        {
            action();
            Assert.Fail(
                "Expected '{0}' to be thrown but no exception was thrown.",
                typeof(T).FullName
            );
        }
        catch (T)
        {
            return;
        }
        catch (AssertFailedException)
        {
            throw;
        }
        catch (Exception e)
        {
            Assert.Fail("Expected '{0}' to be thrown, but '{1}' was thrown instead.", 
                        typeof(T).FullName,
                        e.GetType().FullName
            );
        }
    }
}

Remember I told you that I'd be taking care of that ExpectedExceptionAttribute attribute. This is one instance where NUnit and xUnit really has something on MS Test (syntactically), namely the Assert.Throws<T>(...) method. I hate the ExpectedExceptionAttribute now because it messes with the Arrange, Act, and Assert pattern of TDD. Plus, it can mask other problems with your code. So instead, I get rid of the attribute and use a custom static class modeled after the Assert and CollectionAssert classes in the standard MS Test test framework. So, in my test above, I added a call to my new assertion method trying to create a category with a whitespace only name. The constructor currently only looks for null and the empty string, so this test will fail. It's a simple matter of updating the guard clause in the constructor to make this test pass:

public Category(string name)
{
    if (string.IsNullOrWhiteSpace(name))
        throw new InvalidCategoryNameException();

    Name = name;
}

That's it. The test above should now pass and I've ensured that the Category class is enforcing all of the invariants on its name. There are a few more tests I need to write before I get to the concept of creating context classes. I'm going to skip the step-by-step, but I will show you the final tests that I created to enforce some of the category's Subcategory property invariants (the ones that I don't enforce will be enforced later, as I continue to develop this class). I additionally included a test that ensures that any whitespace that appears in a category name is replaced with a single space character. I'll also show you the Category class as it stands after having written the tests.

[TestClass]
public class CategoryTests
{
    private string PARENT;
    private Category parent;

    [TestInitialize]
    public void CategoryTestsSetup()
    {
        // It's important to initialize test state here, because if a test 
        // (in-)advertently changes the variable's state, it could affect 
        // other tests.
        PARENT = "Parent";
        parent = new Category(PARENT);
    }

    // ...

    // Our other tests we wrote previously are up above.

    [TestMethod]
    public void
    A_category_name_is_sanitized_to_contain_only_1_space_character_between_words()
    {
        AssertCategoryNameWhitespaceIsCollapsed(
            "A  category  with  extraneous  whitespace", 
            "A category with extraneous whitespace"
        );
        AssertCategoryNameWhitespaceIsCollapsed(
            "A\r\ncategory\twith\vother\fwhitespace characters", 
            "A category with other whitespace characters"
        );
        AssertCategoryNameWhitespaceIsCollapsed(
            "This category name is ok", 
            "This category name is ok"
        );
    }

    private void
    AssertCategoryNameWhitespaceIsCollapsed(string name, string expectedName)
    {
        Assert.AreEqual(new Category(name).Name, expectedName);
    }

    [TestMethod]
    public void A_top_level_category_returns_its_name_when_ToString_is_called()
    {
        Assert.AreEqual(PARENT, parent.ToString());
    }
}

Now for the Category class as it stands now.

public class Category
{
    public static readonly Category None = new NullCategory();
    private string name;
    private List<Category> subCategories;

    protected Category()
    {
        subCategories = new List<Category>();
    }

    public Category(string name) : this()
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new InvalidCategoryNameException();

        this.name = SanitizeName(name);
    }

    public virtual string Name
    {
        get { return name; }
        set { name = SanitizeName(value); }
    }

    public IReadOnlyCollection<Category> Subcategories 
    {
        get { return subCategories.AsReadOnly(); }
    }

    public virtual void AddSubcategory(Category c) 
    {
        if (this == Category.None)
            throw new InvalidOperationException(
                "Unable to add a subcategory to the 'None' category."
            );

        if (c == null || c == Category.None)
            throw new InvalidSubcategoryException();

        if (subCategories.Any(sc => sc.Name == c.Name))
            throw new DuplicateSubcategoryException();

        subCategories.Add(c);
    }

    private string SanitizeName(string proposedName)
    {
        return Regex.Replace(proposedName, @"\s+", " ");
    }

    #region System.Object overrides

    public override string ToString()
    {
        return Name;
    }

    #endregion

    /// <summary>
    /// This class represents an implementation of the Null Object pattern 
    /// (a/k/a Special Case pattern) and is meant to be used anywhere
    /// where you might otherwise return null.
    /// </summary>
    private sealed class NullCategory : Category
    {
        public override Name
        {
            get { return string.Empty; }
            set { }
        }
    }
}

So ordinarily, I would have written two unit tests for each of the invalid subcategory conditions asserted in the tests above; but instead, I chose to use a custom assertion method and perform one logical assert. You can also see that I have two new private member variables in the test class which are initialized before each test. But only two of the three tests need these private members. Now, ordinarily, this isn't all that bad and I wouldn't bother with factoring out a context class. But what if the tests were much more involved in the setups that are needed? For instance, at my place of employment, I recently had 7 tests, 2 of which required the same 20 or 30 lines of setup code, the other 5 of which required at least another 10 common lines of setup code between them. All of this code would have been repeated in the methods requiring the code. Or worse, all 50 or so lines would have been in a single setup method, with all of that setup occurring before each test, even if a particular test didn't require all that was setup.

You can also see more fleshed out versions of the Category class. I also created a nested, private category class that is an implementation of the Null Object (a/k/a Special Case) Pattern to represent the null category. Implementing this pattern allows programmers to not have to check for null all the time before accessing properties and fields of objects. Because it's a nested, private class, it cannot be instantiated by anything except the Category class. This allows the NullCategory to be exposed as a public static field on the Category class as a Singleton.

Now, I have not addressed setting the parent category of a category. By default, a category's parent is set to the Singleton NullCategory. But before I tackle that, I want to override the System.Object.Equals method. And when I override this method, I also need to override the System.Object.GetHashCode method and the equality and inequality operators.

The Equality Context Test Class

My motivation for overriding the Equals method and the (in)equality operators is for easy comparison of two categories in order to determine whether or not they are the same. Also, the implementation I have in mind will prevent two subcategories with the same name being added to the same parent. I think you'll see how I accomplish this very easily without much further explanation, so let's hop to it.

Of course, before I can write any code to implement equality testing, I'll need to write the tests. In order to test equality, I'll need two categories. But I'll only need them for this set of tests. I don't want to drag these categories around for each and every other test I might have surrounding categories. So, I think it's best at this point to create a context class for testing category equality. (Again, this example is very simplistic and contrived, and probably not worthy of this level of breakdown; but keep in mind the real-life scenario I mentioned above with 50 lines of code needed by some of the various tests throughout a single CUT. The benefits are potentially huge: smaller test setups; faster tests; smaller, easier to read, and maintainable tests.)

Ok, so below are the tests for the Category class I've been working with (somewhat abbreviated), showing the new context class. I'll also show you my implementation of the equality method and operators in the abstract category class.

[TestClass]
public abstract class CategoryTests
{
    private string PARENT;
    private Category parent;

    [TestInitialize]
    public void CategoryTestsSetup()
    {
        // It's important to initialize test state here, because if a test 
        // (in-)advertently changes the variable's state, it could affect 
        // other tests.
        PARENT = "Parent";
        parent = new Category(PARENT);
    }

    // ...

    // Our other tests we wrote previously are up above.
    [TestClass]
    public class CategoryEqualityContext : CategoryTests
    {
        Category categoryA;
        Category categoryB;

        [TestInitialize]
        public void CategoryEqualityContextSetup()
        {
            categoryA = new Category("A");
            categoryB = new Category("B");
        }

        // This method was moved from the outer context because ToString is 
        // used in determining equality, so it makes sense to move it to this
        // context.
        [TestMethod]
        public void A_top_level_category_returns_its_name_when_ToString_is_called()
        {
            Assert.AreEqual("A", categoryA.ToString());
        }

        [TestMethod]
        public void
        Can_determine_whether_or_not_two_categories_are_equal_to_one_another()
        {
            Assert.AreEqual(categoryA, categoryA);
            Assert.IsTrue(categoryA.Equals(categoryA));
            Assert.IsTrue(((object)catgeoryA).Equals((object)categoryA));
            Assert.IsTrue(categoryA.Equals(new Category("A")));
        }

        [TestMethod]
        public void
        Can_use_the_equality_operator_to_determine_whether_two_categories_are_equal_to_one_another()
        {
            Category c = new Category("A");
            Assert.IsTrue(c == categoryA);
        }

        [TestMethod]
        public void
        Can_use_the_inequality_operator_to_determine_whether_two_categories_are_not_equal_to_one_another()
        {
            Assert.IsTrue(categoryA != categoryB);
        }
    }
}

So let's go over these tests before we get to the implementation in Category. The way MS Test works with nested classes like this is that the outer class is instantiated first, then the inner class, just like any other nested class structure in C#. But in addition to that, the outer class's method marked with the TestInitializeAttribute attribute is also run prior to the nested class's method marked with the same attribute. If the nested class doesn't define such a method, the outer class's method still runs before each and every test in the inner class. So what does that mean for these tests? Well, in this instance, it means that the PARENT string and parent category are carried along into the CategoryEqualityContext. While in this trivial example this isn't a big deal, this is not ideal in a situation where you have a lot of setups occurring in the outer class which the inner class methods don't need. Actually, we're going to refactor our tests to remove the outer class setups in just a bit. But I wanted to show you this so I could explain how the nested context classes work.

Secondly, notice that I've made the CategoryTests class abstract. This is very important. The CategoryEqualityContext class inherits from the CategoryTests class. This also means that it inherits all the methods of its base class. So, let's assume that the CategoryTests class isn't abstract and follow the flow of execution. The CategoryTests class is marked with the TestClassAttribute attribute, so MS Test looks for all the test methods in that class and executes them. Then MS Test sees a public, nested class called CategoryEqualityContext and finds all of its test methods and executes them. But because the CategoryEqualityContext class inherits from CategoryTests class, the tests in CatgoryTests run again as part of CategoryEqualityContext. Now you can see why it's very important to make the CategoryTests class abstract. The general rule of thumb, then, is the following:

If you have nested context test classes, make all test classes abstract except the inner-most context class; leave that class concrete. This ensures that your tests execute only once.
There is one more caveat to using nested context classes for scoping your test setups; I'll get to that later in this blog post.

OK, now that you know how nested context classes work, I'm going to show you how I implemented equality checking in the Category class to make the above tests pass.

public class Category
{
    // ...
    // All other methods previously shown are ansumed to exist above.

    public bool Equals(Category other)
    {
        if ((object)other == null)
            return false;

        return this.ToString() == other.ToString();
    }

    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;

        Category c = obj as Category;
        if ((object)c == nul)
            return false;

        return this.ToString() == c.ToString();
    }

    public override int GetHashCode()
    {
        return this.ToString().GetHashCode();
    }

    public static bool operator ==(Category a, Category b)
    {
        if (object.ReferenceEquals(a, b))
            return true;

        if ((object)a == null || (object)b == null)
            return false;

        return a.ToString() == b.ToString();
    }

    public static bool operator !=(Categoriy a, Category b)
    {
        return !(a == b);
    }
}

So there's not too much to be said about my implementation above. It pretty much follows the standard suggested implementation. Equality merely consists of checking whether or not 1) the acutal objects are the same object reference, or 2) the ToString method returns the same string. The only thing to note is that I have no concept of a parent category yet. When I finally add the ability to set a parent category, this will change my implementation of ToString.

Even More Nested Context Classes

So now you have an idea of how to use nested context classes in order to organize your test setups. While the simple example above gave you an at a glance view, let's make this a bit more concrete by implementing the rest of the functionality. To do that, let me tell you some other rules surrounding categories. First, you cannot have two subcategories of a single category that have the same name. You cannot add the None category as a subcategory (we already covered that). A category's parent cannot be null, but it can be None, meaning it's a "top-level" category. In order to make checking for duplicate subcategories really quick and easy, we're going to change the implementation of ToString. The implementation I have in mind will afford a couple of benefits:

  • Allows very quick determination of whether or not a category can be added as a subcategory
  • Allows very quick determination of whether or not two categories are the same by checking their path
  • Disallows cycles, e.g. A -> B -> C -> A does not form a cycle.


A note about the cycle point above: we'll have a method that ensures that the new parent isn't already a descendant of the current category. The method that's used is recursive. Technically, if you have extremely many levels of nested categories (e.g. A -> B -> C -> D -> ... etc.), it's possible to blow out the stack. But generally speaking, a product catalog for instance, will usually only have 2 - 5 levels of nesting; and that's being generous on the high-end. I guess a way to avoid a potential StackOverflowException would be to limit the allowable amount of nesting. But given the general use-case for this object—and mostly because this is just an example for this blog article and not something I'd use in production code—I'm not inclined to do so. I'll leave it to you as an exercise. Also, my current implementation of AddSubcategory will need to change. We'll also add a new constructor to the class.

Again, even though I told you what I'm going to implement in the class, all of the implementation was based on the tests that I wrote. So here are the new (and final) set of tests.

[TestClass]
public class CategoryTests
{
    // ...
    // All the other tests in the outer context are above.
    // The original setup method was moved to another nested 
    // context to be shown below, along with the test that
    // required the setups.

    [TestClass]
    public abstract class CategoryEqualityContext : CategoryTests
    {
        // ...
        // Nothing has changed in this test class from above.
    }

    [TestClass]
    public abstract class ParentCategoryContext : CategoryTests
    {
        // These variables and the setup method were moved from
        // the outer CategoryTests context class
        private string PARENT;
        private Category parent;

        [TestInitialize]
        public void ParentCategoryContextSetup()
        {
            PARENT = "Parent";
            parent = new Category(PARENT);
        }

        [TestMethod]
        public void Can_add_a_subcategory_to_a_category()
        {
            parent.AddSubcategory("subcategory");
            Assert.AreEqual(1, parent.Subcategories.Count);
        }

        [TestMethod]
        public void Cannot_add_a_duplicate_subcategory_to_a_category()
        {
            parent.AddSubcategory("subcategory");
            ExceptionAssert.Throws<DuplicateSubcategoryException>(
                () => parent.AddSubcategory("subcategory")
            );
        }

        [TestMethod]
        public void A_top_level_category_returns_its_name_when_ToString_is_called()
        {
            Assert.AreEqual(PARENT, parent.ToString());
        }

        [TestClass]
        public class ChildCategoryContext : ParentCategoryContext
        {
            private string CHILD;
            privte Category child;

            [TestInitialize]
            public void ChildCategoryContextSetup()
            {
                CHILD = "Child";
                child = parent.AddSubcategory(CHILD);
            }
    
            [TestMethod]
            public void
            A_subcategory_returns_its_parents_name_with_a_path_separator_and_its_own_name_when_ToString_is_called()
            {
                Assert.IsTrue(string.Concat(PARENT, "/", CHILD) == child.ToString(), 
                    string.Format("Expected '{0}/{1}' but got '{2}'", PARENT, CHILD, child.ToString()
                );
            }

            [TestMethod]
            public void
            Cannot_set_category_parent_if_new_parent_is_a_descendant_of_the_category()
            {
                Category grandchild = child.AddSubcategory("Grandchild");

                // Attempt parent/child/grandchild/parent where 'parent' 
                // at both the head and tail of the list are the same object.
                ExceptionAssert.Throws<invalidoperationexception>(() => parent.Parent = grandchild);
            }
        }
    }
}

After implementing the code that ought to make these tests pass, one of the tests above will actually fail. Can you spot which test will fail? Don't try too long to find it. It's really subtle, and is one of a small list of cons when using this style of testing. The test that will fail is A_top_level_category_returns_its_name_when_ToString_is_called(). But why should that fail? It looks just fine. Again, it's really subtle. The good news is that the test fails and doesn't produce a false positive (e.g. a passing test that should fail). Remember the rule I stated above concerning derived test classes. The most derived test class should be concrete while all lesser derived test classes and the base class must be abstract. In the concrete test class in the ChildCategoryContextSetup() method, I call parent.AddSubcategory(CHILD);. So a new subcategory is added to the parent category. So the parent category now contains 1 subcategory (because the setup runs prior to any tests executing). So now when I execute the A_top_level_category_return_its_name_when_ToString_is_called(), an additional subcategory is added to the parent category (for a total of 2 subcategories) and the assertion fails. There are two ways to fix this:

  1. Move the test to the ChildCategoryContext class. I don't like this option because to me, it seems like since it's only using the parent category, it should reside in the ParentCategoryContext class.
  2. Obtain the count of subcategories of the parent category prior to adding a subcategory. Then assert that the count of subcategories is equal to one plus the count obtained before acting on the CUT. To me, this is a much better option. The test is still straight-forward; the class resides where you'd expect it to reside; the test is less fragile.


I think I'll go with option number two and re-write the test as follows:

[TestMethod]
public void A_top_level_category_returns_its_name_when_ToString_is_called()
{
    int numSubcategories = parent.subCategories.Count;
    parent.AddSubcategory("subcategory");
    Assert.AreEqual(numSubcategories + 1, parent.Subcategories.Count);
}

With that done, that's it for the tests. Now for the implementation in the Category class.

public class Category
{
    // ...
    // All the old fields are above

    private Category parentCategory;

    // The constructors are have changed a bit.

    protected Category()
    {
        subCategories = new List<Category>();
    }

    public Category(string name) : this(name, None) { }

    // This constructor is private so that we can enforce not allowing
    // cycles to occur in parent/child/grandchild relationships.
    private Category(string name, Category parent) : this()
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new InvalidCategoryNameException();
        
        // You shouldn't call virtual properties and methods from your constructors,
        // so use the fields directly.
        this.name = SanitizeName(name);

        if (parentCategory == null)
            parentCategory = parent;
        else // Use SetParentCategory(...) to prevent cyclic relationships.
            SetParentCategory(parent);
    }

    // ...
    // The other properties listed before remain the same.

    public virtual Category Parent
    {
        get { return parentCategory; }
        set 
        {
            // Since on creation parentCategory is never left null, 
            // always call this method when setting the parent.
            SetParentCategory(value);
        }
    }

    // The original implementation of AddSubcategory was made private so
    // that external users can no longer pass in an arbitrary category.
    // This prevents cycles in the hierarchy.
    // 
    // The new public method is now a sort of factory method that creates
    // a new category, adding it as a subcategory to the current instance,
    // and returning the newly creatd category.

    private void AddSubcategory(Category c)
    {
        // ...
        // The original method stays the same...it's just private now.
    }

    /// <summary>
    /// Creates a new Category as a subcategory of the current instance.
    /// </summary>
    /// <param name="name">The name of the new subcategory.</param>
    /// <exception cref="InvalidOperationException">
    ///     Thrown if the current instance is <see cref="Category.None"/>.
    /// </exception>
    /// <exception cref="InvalidCategoryNameException">
    ///     Thrown if the <paramref name="name"/> is <c>null</c>,
    ///     the empty string, or consists entirely of whitespace.
    /// </exception>
    public virtual Category AddSubcategory(string name)
    {
        if (this == None)
            throw new InvalidOperationException();
        
        Category subcategory = new Category(name, this);
        AddSubcategory(subcategory);
        return subcategory;
    }

    private void SetParentCategory(Category c)
    {
        if (c != Category.None && this.HasDescendant(c))
            throw new InvalidOperationException();

        parentCategory = c;
        if (parentCategory == Category.None)
            parentCategory.AddSubcategory(this);
    }

    // This method determines whether or not the new parent passed to
    // SetParentCategory is a descendant of the category on which
    // SetParentCategory was called. If so, this method returns true.
    // This prevents cycles in the hierarchy.
    // Again, limiting hierarchy depth can prevent possible stack overflows
    // due to the recursive nature of this function.
    private bool HasDescendant(Category c)
    {
        bool result = false;

        if (this == c)
            result = true;
        else if (c.Parent != Category.None)
            result = this.HasDescendant(c.Parent);

        return result;
    }

    // The implementation of this method changed. Instead of simply returning
    // the name of the category, it returns the category's path from the root:
    // e.g. A/B/C, or Parent/Child/Grandchild.
    public override string ToString()
    {
        return Parent == Category.None ?
            Name : string.Concat(Parent.ToString(), "/", Name);
    }
}

There you go, that wraps up all of the code for the Category class. Let's just take a few seconds to review it. We finally added a Parent property. In addition, we added a private constructor that creates a new category and sets its parent. This constructor is private so that I have control over preventing cycles in the hierarchy. The original constructor that takes a category name defers to this new constructor, setting the parent category to Category.None. We made the original implementation of AddSubcategory private. The original implementation allowed you to pass in any Category instance, which could allow cycles to occur. So a new method was added that acts as a Factory Method, adding the newly created category as a subcategory of the instance on which it was called and returning the newly created subcategory. The SetParentCategory method is also interesting. It checks that if the category passed into it is not Category.None, then that the category is also not a descendant of the current instance—again, preventing cycles in the hierarchy. Finally, the ToString implementation changed to return the full path of the category from the root of its hierarchy, e.g. Parent/Child/Grandchild.

Conclusion

Again, the Category class is fairly small. Having said that, this ended up being a very long blog article. But I think that because of its relative simplicity, it made a good example of how using nested test classes to provide context boundaries for test setup methods can help you keep your setup methods small. Using context classes and setup methods, and keeping your setup methods small promotes the following benefits:

  • Small setups: Smaller setups mean that less data is being instantiated and configured; and only the data required for the tests in the current context is setup. This has additional benefits:
    • Faster execution times (OK, this could be anywhere from negligible to very noticeable)
    • Less chance for contamination to occur between tests, e.g. mutated state affecting downstream tests (though with the caveat seen earlier in this post)


  • Using setup methods allows you to keep your tests DRY
  • Using context classes in addition to setup methods keeps your tests small and easy to read
  • Small tests are focused, and usually adhere to the SRP. This means your tests are:
    • easy to maintain
    • flexible (read, not fragile)


  • By going one step further and using composable assertions, you make your tests even more understandable, easier to maintain, and less fragile.

There is a corresponding list of cons when it comes to using this style of testing, so to be fair, I'll list out the ones that I can immediately see.

  • Deep nested classes and the indenting could be annoying
  • Tests might be hard to find (heck, there's always CTRL+F)
  • If you're not careful with your assumptions, derived class setups can interfere with base class tests
    • This is why it's even more important to not just start writing your tests in this style, but after you've written a few and refactored a bit, you can be more confident about the arrangement of your contexts.
  • You have to remember to mark all base-class tests abstract and leave the inner-most class concrete.
  • Not all test runners support abstract base classes (AutoTest.Net for one)


I'm sure you might be able to think of some other pros or cons. Hopefully, though, you'll agree that the benefits listed above may be worth the little bit of extra effort. Before leaving you, I want to touch a little more on the effort that went into making these tests hierarchical.

The effort to make these hierarchical test classes was very small. Granted, this example class is not that complex and not that large. At my current place of employment, I had already used this concept. It did take me a bit of time. But that was the first time ever that I attempted this. This example in this article was the second time I attempted this, and it went much much faster. Why? Because I now know what to look for. There are a few things I want you to keep in mind if you want to try this style of testing:

  1. Don't attempt to start creating context test classes right off the bat. You will most likely fail.
  2. As Robert Martin says, treat your test code as you would your production code.
    • This means that you will refactor your tests as you go along in your development effort.
    • It's during this refactoring that you will begin to see patterns in usage in the data required for your tests.
    • Begin refactoring by simply extracting methods.


  3. Once you begin to see common groupings of functionality, e.g. the same methods being called from various tests or the same data needed by a group of tests, and what not, that's when the creation of context classes will payoff.
  4. Keep these other thoughts in mind when determining the contexts you may need:
    • If tests require the same setup, they belong in a single test class
    • If tests require the same setup as other tests, and also need a few more things (say, more than two..otherwise, just arrange the items in your tests), consider placing them in a nested context class. Remember to make all base classes abstract and the inner-most derived class concrete.
    • If tests require the same setup as other tests, but have additional needed setups different from another nested context classes, create a sibling context class.


I just want to take a minute and explain the last bullet point about sibling contexts. The last point is talking about the same situation we saw even in the small example presented in this post. We had a CategoryTests class that had two nested, sibling context classes: the first nested sibling context class, CategoryEqualityContext required additional setup that CategoryTests did not provide; the second nested sibling context class, ParentCategoryContext, also shared the same setup as CategoryTests but needed additional, yet, different setup than CategoryEqualityContext. Furthermore, the ParentCategoryContext contained yet another nested context class within which required the setups of both CategoryTests and ParentCategoryTest plus additional setup.

Finally, we've come to the end of this very long blog post. I hope that you enjoyed it and that you learned something. If you have a question, feel free to drop me a line and I'll try to get back to you.

Below, find the full, uninterrupted code of the Category class and its corresponding test class(es). Until next time, take care.

using System;
using System.Collections.Generic;
using System.Diagnostices;
using System.Linq;
using System.Text.RegularExpressions;

namespace DDDSportsStore.Domain
{
    [DebuggerDisplay("[Name: {Name}]")]
    public class Category
    {
        private static readonly Category None = new NullCategory();

        private string name;
        private Category parentCategory;
        private List<Category> subCategories;

        protected Category()
        {
            subCategories = new List<Category>();
        }

        public Category(string name) : this(name, Category.None) { }

        private Category(string name Category parent) : this()
        {
            if (string.IsNullOrWhiteSpace(name))
               throw new InvalidCategoryNameException();

            this.name = SanitizeName(name);
            if (parentCategory == null)
                parentCategory = parent;
            else
                SetParentCategory(parent);
        }

        public virtual string Name
        {
            get { return name; }
            set { name = SanitizeName(value); }
        }

        private string SanitizeName(string proposedName)
        {
            return Regex.Replace(proposedName, @"\s+", " ");
        }

        public virtual Category Parent
        {
            get { return parentCategory; }
            set { SetParentCategory(value); }
        }

        private void SetParentCategory(Category c)
        {
            if (c != Category.None && this.HasDescendant(c))
                throw new InvalidOperationException();

            parentCategory = c;
            if (parentCategory == Category.None)
                parentCategory.AddSubcategory(this);
        }

        private bool HasDescendant(Category c)
        {
            bool result = false;

            if (this == c)
                result = true;
            else if (c.Parent != Category.None)
                result = this.HasDescendant(c.Parent);

            return result;
        }

        public IReadOnlyCollection<Category> Subcategories
        {
            get { return subCategories.AsReadOnly(); }
        }

        /// <summary>
        /// Creates a new Category as a subcategory of the current instance.
        /// </summary>
        /// <param name="name">The name of the new subcategory.</param>
        /// <exception cref="InvalidOperationException">
        ///     Thrown if the current instance is <see cref="Category.None"/>.
        /// </exception>
        /// <exception cref="InvalidCategoryNameException">
        ///     Thrown if the <paramref name="name"/> is <c>null</c>,
        ///     the empty string, or consists entirely of whitespace.
        /// </exception>
        public virtual Category AddSubcategory(string name)
        {
            if (this == None)
                throw new InvalidOperationException();
        
            Category subcategory = new Category(name, this);
            AddSubcategory(subcategory);
            return subcategory;
        }

        private void AddSubcategory(Category c) 
        {
            if (this == Category.None)
                throw new InvalidOperationException(
                    "Unable to add a subcategory to the 'None' category."
                 );

            if (c == null || c == Category.None)
                throw new InvalidSubcategoryException();

            if (subCategories.Any(sc => sc.Name == c.Name))
                throw new DuplicateSubcategoryException();

            subCategories.Add(c);
        }

        public bool Equals(Category other)
        {
            if ((object)other == null)
                return false;

            return this.ToString() == other.ToString();
        }


        #region System.Object overrides

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            Category c = obj as Category;
            if ((object)c == nul)
                return false;

            return this.ToString() == c.ToString();
        }

        public override int GetHashCode()
        {
            return this.ToString().GetHashCode();
        }

        public override string ToString()
        {
            return Parent == None ?
                Name : string.Concat(Parent.ToString(), "/", Name);
        }

        #endregion

        #region Operator Overrides

        public static bool operator ==(Category a, Category b)
        {
            if (object.ReferenceEquals(a, b))
                return true;

            if ((object)a == null || (object)b == null)
                return false;

            return a.ToString() == b.ToString();
        }

        public static bool operator !=(Categoriy a, Category b)
        {
            return !(a == b);
        }

        #endregion

        #region NullCategory

        /// <summary>
        /// This class reperesents an implementation of the Null Object
        /// pattern (a/k/a Special Case pattern) and is meant to be used
        /// anywhere where you might otherwise return null.
        /// </summary>
        private sealed class NullCategory : Category
        {
            public override Name
            {
                get { return string.Empty; }
                set { }
            }
        }
    }

    #endregion
}

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace DDDSportsStore.Domain.Tests
{
    [TestClass]
    public class CategoryTests
    {
        [TestMethod]
        public void Cannot_create_a_Category_with_an_invalid_name()
        {
            AssertCannotCreateCategoryWithName(null);
            AssertCannotCreateCategoryWithName(string.Empty);
            AssertCannotCreateCategoryWithName("\n");
        }

        [TestMethod]
        public void
        A_category_name_is_sanitized_to_contain_only_1_contiguous_whitespace_character_between_words()
        {
            AssertCategoryNameWhitespaceIsCollapsed(
                "A  category  with  extraneous  whitespace",
                "A category with extraneous whitespace"
            );
            AssertCategoryNameWhitespaceIsCollapsed(
                "A\r\ncategory\twith\vother\fwhitespace characters",
                "A category with other whitespace characters"
            );
            AssertCategoryNameWhitespaceIsCollapsed(
                "This category name is ok", 
                "This category name is ok"
            );
        }

        private void AssertCannotCreateCategoryWithName(string name)
        {
            ExceptionAssert.Throws<InvalidCategoryNameException>(
                () => new Category(name)
            );
        }

        private void
        AssertCategoryNameWhitespaceIsCollapsed(string name, string expectedName)
        {
            Assert.AreEqual(new Category(name).Name, expectedName);
        }

        public class CategoryEqualityContext : CategoryTests
        {
            Category categoryA;
            Category categoryB;

            [TestInitialize]
            public void CategoryEqualityContextSetup()
            {
                categoryA = new Category("A");
                categoryB = new Category("B");
            }

            [TestMethod]
            public void
            A_top_level_category_returns_its_name_when_ToString_is_called()
            {
                Assert.AreEqual("A", categoryA.ToString());
            }

            [TestMethod]
            public void
            Can_determine_whether_or_not_two_categories_are_equal_to_one_another()
            {
                Assert.AreEqual(categoryA, categoryA);
                Assert.IsTrue(categoryA.Equals(categoryA));
                Assert.IsTrue(((object)catgeoryA).Equals((object)categoryA));
                Assert.IsTrue(categoryA.Equals(new Category("A")));
            }

            [TestMethod]
            public void
            Can_use_the_equality_operator_to_determine_whether_two_categories_are_equal_to_one_another()
            {
                Category c = new Category("A");
                Assert.IsTrue(c == categoryA);
            }

            [TestMethod]
            public void
            Can_use_the_inequality_operator_to_determine_whether_two_categories_are_not_equal_to_one_another()
            {
                Assert.IsTrue(categoryA != categoryB);
            }
        }

        [TestClass]
        public class ParentCategoryContext : CategoryTests
        {
            private string PARENT;
            private Category parent;

            [TestInitialize]
            public void ParentCategoryContextSetup()
            {
                PARENT = "Parent";
                parent = new Category(PARENT);
            }

            [TestMethod]
            public void Can_add_a_subcategory_to_a_category()
            {
                int numSubcategories = parent.subCategories.Count;
                parent.AddSubcategory("subcategory");
                Assert.AreEqual(numSubcategories + 1, parent.Subcategories.Count);
            }

            [TestMethod]
            public void Cannot_add_a_duplicate_subcategory_to_a_category()
            {
                parent.AddSubcategory("subcategory");
                ExceptionAssert.Throws<DuplicateSubcategoryException>(
                    () => parent.AddSubcategory("subcategory")
                );
            }

            [TestClass]
            public class ChildCategoryContext : ParentCategoryContext
            {
                private string CHILD;
                privte Category child;

                [TestInitialize]
                public void ChildCategoryContextSetup()
                {
                    CHILD = "Child";
                    child = parent.AddSubcategory(CHILD);
                }
    
                [TestMethod]
                public void
                A_subcategory_returns_its_parents_name_with_a_path_separator_and_its_own_name_when_ToString_is_called()
                {
                    Assert.IsTrue(string.Concat(PARENT, "/", CHILD) == child.ToString(),
                        string.Format("Expected '{0}/{1}' but got '{2}'", PARENT, CHILD, child.ToString());
                }

                [TestMethod]
                public void
                Cannot_set_category_parent_if_new_parent_is_a_descendant_of_the_category()
                {
                    Category grandchild = child.AddSubcategory("Grandchild");
                    ExceptionAssert.Throws<Invalidoperationexception>(
                        () => parent.Parent = grandchild
                    );
                }
            }
        }
    }

    public static class ExceptionAssert
    {
        public static void Throws<T>(Action act) where T : Exception
        {
            try
            {
                act();
                Assert.Fail("Expected '{0}' to be thrown but no exception was thrown.");
            }
            catch (T)
            {
                return;
            }
            catch (AssertFailedException)
            {
                throw;
            }
            catch (Exception e)
            {
                Assert.Fail(
                    "Expected '{0}' to be thrown, but '{1}' was thrown instead.",
                    typeof(T).FullName,
                    e.GetType().FullName
                );
            }
        }
    }
}