Tuesday, 30 July 2013

New Tricks from an Old Pattern

Its great when you can still be surprised by a pattern you think you already know inside-out. I was pleased today to think about the BDD Unit Testing Style 'pattern' in a new way. Here's my previous post on the subject that will give you the necessary background for the rest of this post.

The pattern utilises object inheritance to make it powerful and descriptive. To illustrate, we must start with an example. The code below contains a method that tests two conditions and tries to return an object. If both conditions fail then the method throws an exception. It is worth noting that the sample class is not a good example of OO design, but rather a simplified case. The important point is that there is a path through the method that will cause an exception to be thrown.

public class ScoreChecker
{
public IPlayer GetScoredPlayer(IPlayer player)
{
if (player.Score > 8)
{
return new HighScorePlayer(player);
}
if (player.Score > 2)
{
return new LowScorePlayer(player);
}
throw new ScoreTooLowException();
}
}
view raw ScoreChecker.cs hosted with ❤ by GitHub
Using the BDD Style Unit Testing pattern makes it pretty easy to test the first two conditions and the object that is returned in each case. Note the use of an abstract base class which provides us with the Result variable to hold our return object and importantly encapsulates away the call to the When() method. The Class is called WhenIGetScoredPlayer, meaning we should expect to find the call to GetScoredPlayer() on the Target inside of the overriden When() method (see the preceding article if you aren't following).

This means we don't have to repeat this call to the same target method in any of the classes that inherit from this base class. It makes the classes easy to navigate and the test output becomes straight forward as coherent sentences are produced: "When I Get Scored Player, Given The Player Has a Score Greater Than 8, Then the Result is a High Score Player". This is useful information when reviewing tests results.

public abstract class WhenIGetScoredPlayer : GivenA<ScoreChecker>
{
protected readonly Mock<IPlayer> MockPlayer = new Mock<IPlayer>();
protected IPlayer Result = null;
protected override void When()
{
base.When();
Result = Target.GetScoredPlayer(MockPlayer.Object);
}
}
public class GivenThePlayerHasAScoreGreaterThan8 : WhenIGetScoredPlayer
{
protected override void Given()
{
base.Given();
MockPlayer.Setup(m => m.Score).Returns(9);
}
[Then]
public void TheResultIsAHighScorePlayer()
{
Assert.IsInstanceOf<HighScorePlayer>(Result);
}
}
public class GivenThePlayerHasAScoreGreaterThan2 : WhenIGetScoredPlayer
{
protected override void Given()
{
base.Given();
MockPlayer.Setup(m => m.Score).Returns(3);
}
[Then]
public void TheResultIsALowScorePlayer()
{
Assert.IsInstanceOf<LowScorePlayer>(Result);
}
}
However, you will now notice that its not so simple in the third path of this method. That's because in the case when a player scores below 2 an exception is thrown. If I inherit from the abstract WhenIGetScoredPlayer class and setup the player's score accordingly, then the exception will be thrown too early. That's because in the base GivenA<T> class a call to the virtual When() follows a call to the virtual Given() in a method tagged with [SetUp], to make sure they happen in sequence as part of the framework - its useful for us to have things happen in this order.

I could start from scratch and perform all the setup in a new class. Making the call to
GetScoredPlayer() inside of a [Test] method instead of as part of [SetUp]. But I don't want to duplicate unnecessarily. This is when I thought about the old pattern in a new way - using an override to postpone the call instead of starting all over again.

What I did was to still implement the base class with the appropriately named GivenThePlayerHasAScoreLowerThan2. The trick is to simply override the base When() and leave it empty. This prevents it from calling the GetScoredPlayer() during the [SetUp] phase of the test. It can now occur in the [Test] method, wrapped in an NUnit assertion that the correct type of Exception is thrown. Producing the desired output without unnecessary duplication: "When I Get Scored Player, Given The Player Has a Score Lower Than 2, A Score Too Low Exception Is Thrown".

public class GivenThePlayerHasAScoreLowerThan2 : WhenIGetScoredPlayer
{
protected override void Given()
{
base.Given();
MockPlayer.Setup(m => m.Score).Returns(1);
}
protected override void When()
{
// prevent base.When() from being called
// and throwing an exception too early!
}
[Then]
public void AScoreTooLowExceptionIsThrown()
{
Assert.Throws<ScoreTooLowException>(() => base.When());
}
}