Poor Man's Test Context Composition

Or: Mixin strategies in the fields

Before I continue on my little post about Unit Tests and their test context, let me just be one thing very clear: A handful of unit test frameworks out there have dozens of other approaches for complex context setup. The one I'm going to blog about right now is just one out of those. The following context composition method is rare to be found, because it has some obvious downsides. More about the pros and cons later, let's just start.

With TDD, BDD and Unit Testing in general you always need to set the stage of your test. That is, you need to establish a test context, where you prepare your environment and all involved parties for your system under test. Basically it's the first A (= Arrange) of the AAA-Rule. Now that's nothing new.

In everyday testing, contexts can get fairly complex. Although I personally consider a complex (or large) context to be a smell, it's sometimes unavoidable. One practical application of large and complex contexts is the dissection of a brownfield. Once you're faced with a large context, your test/spec will be bloated by contextual setup.

As mentioned before, some frameworks have very smart tooling and concepts to help you minimize the context clutter. I'll post a separate blog about one of my favorites, the MSpec and MFakes bundle. But this is a story of its own, so we just disregard them for a minute. By the way: in some organizations you might not be even able to just change the testing framework of your choice and need to get along with what you have been provided.

Context is super !?!

As for "traditional" frameworks like NUnit or XUnit (me likes both of them), the Setup method, constructor or whatever initialization method contains the context complexity to its full extent. Hence, your test fixtures will grow in size and complexity. Undesirable.

A very common solution to this is to refactor out context code to an abstract base "context" class. Although most developers do this primarily for readability purposes, the base class is created to favor reuse as well. Which is an illusion, actually. Once following that path, you quickly find your self back in bewildered inheritance hierarchies of "BaseContextClasses". The tests themselves get more and more obfuscated.

Let's examine. A typical base context class might look like this:

[TestFixture]
public abstract class With_CashMachine_Having_100Dollars {

  [TestFixtureSetUp]
  public void Setup() {
    CashMachine = MockRepository.DynamicMock();
    CashMachine
      .Stub(m => m.CheckBalance())
      .Return(100.Dollar());
  }

  protected ICashMashine CashMachine { get; set; }
}

With the above "context base class", a BDD-style test reads quite expressive:

[TestFixture]
public class When_ATM_Checkout_Request_With_200Dollar_Is_Performed
  : With_CashMachine_Having_100Dollar {

  [Test]
  public void Then_Checkout_Should_Fail_With_CashMashine_Failure() {
    var atm = new ATM(CashMashine);
    var checkoutResult = atm.Checkout(200.Dollar());
    Assert.That(checkoutResult.Failure, Is.EqualTo(FailureCode.CashMashine));
  }
}

Bold Backgrounds

Now, the above ATM example is good to serve as fictional example. Nonetheless, reality is complex. Sometimes, it is by far more complex than the above glorified ATM. In every-day use-case-scenarios, a broad set of dependecies play a note in the overall context orchestra. That is, why test context bases start to become a difficult task.

What if the ATM does not only respect the CashMachine, but as well User, Location, Configuration and Time for this particular scenario? How can we construct this broad set of contextual dependencies while keeping up a high readability and maintainability for the tests/specs? A typical approach is to create "the super-context":

[TestFixture]
public abstract class With_CashMashine_In_Europe_By_Night_Using_Strict_Policies_Having_100Dollar {

  [TestFixtureSetUp]
  public void Setup() {
    CashMachine = MockRepository.DynamicMock();
    CashMachine
      .Stub(m => m.CheckBalance())
      .Return(100.Dollar());

    Location = MockRepository.DynamicMock();
    Location
      .Stub(m => m.Area)
      .Return(ATMDistributionArea.Europe);

    /* aso. aso. ... 
     * you get the picture, huh? 
     **/    
  }

  protected ICashMashine CashMachine { get; set; }
  protected IATMLocation Location { get; set; }
  /* ... */
}

A fair thing, isn't it? Well, apparently the context name (class name) is long. And it's too specific. Whenever one of my contextual members change, the context changes as well. Consequently, a new super-base-context and redundant code for context establishment is required.

Ok. Naturally, there's room for improvement. Obviously, parameterization of values would be a plus. So, let's refactor the values out of the context class:

[TestFixture]
public abstract class With_CashMashine_And_Location_And_DayTime_And_Config {

  [TestFixtureSetUp]
  public void Setup() {
    CashMachine = MockRepository.DynamicMock();

    Location = MockRepository.DynamicMock();
    Location
      .Stub(m => m.Area)
      .Return(ATMDistributionArea.Europe);
  }

  protected ICashMashine GetCashMachine(int balance) {
    CashMashine.BackToRecord();
    CashMachine
      .Stub(m => m.CheckBalance())
      .Return(balance.Dollar());
    CashMashine.Replay();
  }

  protected IATMLocation GetLocation(ATMDistributionArea area) {
    Location.BackToRecord();
    Location
      .Stub(m => m.Area)
      .Return(area);
    Location.Replay();
  }

  /* etc. ... etc. ... */
}

Parameterization relaxes contextual strictness while giving back flexibility (and responsibility) to the fixture class. Granted, it might sound a bit picky. It is however a big difference when your test suite is large. With the parametrized context base class from above, a fixture might look like this one:

[TestFixture]
public class When_ATM_Checkout_Request_With_200Dollar_Is_Performed
  : With_CashMashine_And_Location_And_DayTime_And_Config {

  private static readonly ICashMashine 
    cashMachine = GetCashMashine(100.Dollar());

  private static readonly IATMLocation
    location = GetLocation(ATMDistributionArea.Europe);

  /* ... aso... aso... 
   * side note: i could have done this
   * in a method/constructor as well.
   **/

  [Test]
  public void Then_Checkout_Should_Fail_With_CashMashine_Failure() {
    var atm = new ATM(cashMashine, location, daytime, config);
    var checkoutResult = atm.Checkout(200.Dollars();
    Assert.That(checkoutResult.Failure, Is.EqualTo(FailureCode.CashMashine));
  }
}

Obviously, this approach is a bit better in terms of flexibility and reduced redundancy. But it's not all shine, there's shadow as well. Now what if the context being composed is dynamic? That is, when a valid context can be made of a variety components and combinations, you end up building base context classes for all combinations. In our trivial ATM example, quite a few context classes for CashMashine, Location and Config may be required.

Composing Contexts With Mixins

As mentioned before, this should not be a big issue when you're in a TDD field. With TDD and adequate regard to SOLID principles, you rarely end up with overly complex contexts. In contrast, when tackling a brown-field and trying to get is covered, you often have complex component interdependencies to tackle.

A (not so common) pattern for composition is to use Mixins. Mixins have a notion to me of being somewhat like a poor mans multiple inheritance. I won't to open pandora's box here and discuss the multiple inheritance religions. So let's just stick with the fact that C# does not support multiple inheritance.

One of the simple - if not the easiest - mixin implementations in C# is to use extension methods over interfaces. It's easy and yet quite manageable. I've been using this type of mixins for quite a long time now in different scenarios and it feels quite intuitive. Let's see how a mixin composed test might actually look like:

[TestFixture]
public class When_Requesting_Checkout_Of_200Dollar : Using_ATM
  , With_CashMashine.In_Context
  , With_Location_Europe.In_Context {

  [Test]
  public void Then_Checkout_Request_Is_Denied() {
    Having.A_CashMashine_With_Balance_Of(100.Dollar());
    Having.A_European_Location();

    Assert.False(The().CanCheckout(200.Dollar()));
  }
}

This actually reads pretty straight-forward, at least. I'm sure you already spotted the trick in the first lines of class declaration. And you're right: With_CashMashine is a class containing the interface In_Context. This is the binding for our little mixin fun. The rest of the game is just sugar. A glimpse on how the context is being defined reveals the mixin trick:

/* traditional base context class with sut */
public class Using_ATM : In_Tests.As_Subject {}

/* context component */
public static class With_CashMashine {
  public interface In_Context : In_Tests.As_Context { }

  public static In_Tests.As_Subject A_CashMashine_With_Balance_Of(this In_Tests.As_Subject subject, Amount amount) {
    ICashMashine cashmashine = MockRepository.DynamicMock();
    cashmashine.Stub(m => m.Balance).Return(amount);
    return subject.WithContext(cashmashine);
  }
}

/* poor man's context composition "framework" */
public static class In_Tests {
  public interface As_Context { }

  public interface As_Subject {
    As_Subject WithContext(T context);
  }

  public class As_Subject<T> : As_Subject {
    private readonly IKernel _kernel = new StandardKernel();

    public T The() {
      return _kernel.Get<T>();
    }

    public As_Subject<T> WithContext(T context) {
      _kernel.Bind<T>().ToConstant(context);
      return this;
    }

    protected As_Subject<T> Having { get { return this; } }
  }
}

Most of the neaty mixin weaving is done through the extension method, which in turn uses the context base class for context establishment (the WithContext call). There's no great magic in the context base class As_Subject itself. It uses an IoC-Container to collect all puzzle items and finally lay them out using the The syntactical sugar method. C'est ca.

Mix it, baby ?!?

Fine, so we mixed our context, tests are looking good and can be read (and understood) easily. Does this mean that this is the way to do test context composition?

Straight Answer: No.

Sometimes, it might be helpful, yes. However, in TDD development it most likely won't be of a great benefit. I always felt a good pay-off when I used this pattern in brown-field safari. For plain development, this slightly sophisticated kind of context establishment felt too heavy for me. Obviously, there's a maintenance and controllability issue with this approach. That said, I haven't seen the usage of mixins in context establishment anywhere else, which in turn is the primary reason I wanted to share this pattern with you.

Conclusion: Mixins as test context composition strategy can be beneficial, if used wisely and rarely. It can be a good interim stage method to get existing systems under test.


(c) Copyright 1998 - 2014 Ilker Cetinkaya.