Tuesday 25 June 2013

How to start with a test, not implementation - part 2

(this post is adapted from my work-in-progress open source TDD tutorial)

(Note that I use "Statement" instead of "test" and "Specification" instead of "Test Suite" in this post)

In the previous part, I discussed how a good name is a great start for writing a Statement when there's no production code to invoke. Today, I'll introduce to you another technique.

Start by filling the GIVEN-WHEN-THEN structure with the obvious

This is applicable when you come up with a GIVEN-WHEN-THEN structure for the Statement or a good name for it (GIVEN-WHEN-THEN structure can be easily derived from a good name and vice versa). Anyway, this method is about taking the GIVEN-WHEN-THEN parts and translating them almost literally into code, then add all the missing pieces that are required for the code to compile and run.

An example speaks a thousand words

Let's take a simple example of comparing two users. We assume that a user should be equal to another when it has the same name as the other one:

GIVEN a user with any name
WHEN I compare it to another user with the same name
THEN it should appear equal to this other user

Let's start with the translation

The first line:

GIVEN a user with any name

can be translated literally to code like this:

var user = new User(anyName);

Then the second line:

WHEN I compare it to another user with the same name

can be written as:

user.Equals(anotherUserWithTheSameName);

Great! Now the last line:

THEN it should appear equal to this other user

and its translation into the code:

Assert.True(areUsersEqual);

Ok, so we've made the translation, now let's summarize this and see what's missing to make this code compile:

[Fact] public void 
ShouldAppearEqualToAnotherUserWithTheSameName()
{
  //GIVEN
  var user = new User(anyName);

  //WHEN
  user.Equals(anotherUserWithTheSameName);

  //THEN
  Assert.True(areUsersEqual);
}

As we expected, this will not compile. Notably, our compiler might point us towards the following gaps:

  1. A declaration of anyName variable.
  2. A declaration of anotherUserWithTheSameName object.
  3. The variable areUsersEqual is both not declared and it does not hold the comparison result.
  4. If this is our first Statement, we might not even have the User class defined at all.

The compiler created a kind of a small TODO list for us, which is nice. Note that while we don't have full compiling code, filling the gaps boils down to making a few trivial declarations and assignments:

  1. anyName can be declared as var anyName = Any.String();
  2. anotherUserWithTheSameName can be declared as var anotherUserWithTheSameName = new User(anyName);
  3. areUsersEqual can be declared and assigned this way: var areUsersEqual = user.Equals(anotherUserWithTheSameName);
  4. If User class does not exist, we can add it by simply stating: public class User {}

Putting it all together:

[Fact] public void 
ShouldAppearEqualToAnotherUserWithTheSameName()
{
  //GIVEN
  var anyName = Any.String();
  var user = new User(anyName);
  var anotherUserWithTheSameName = new User(anyName);

  //WHEN
  var areUsersEqual = user.Equals(anotherUserWithTheSameName);

  //THEN
  Assert.True(areUsersEqual);
}

And that's it - the Statement is complete!

No comments: