We are at the point of writing our first test, but first lets review the whole test first pattern, that is the hopefully well known
Red
Green
Refactor
although it seams to be well known I think there has grown up a lot of myth and folk law around the process that gets in the way of actually carrying out what is on the surface a simple process. But a bit like chess this simplicity hides a complex thought process that has to be practiced and when performed well leads to code that is well designed, extensible and easy to maintain.
Lets take this one stage at a time and work through the first test and see how the process translates into code.
Red
Within this phase we write our test, as will be seen we will also be writing some production code, in order to keep what we create compiling and syntaxially correct, we will not worry about duplication or design at this point, our aim is to write a purposefully failing test.
Our first test is about the creation of a basket, as discussed in the previous post, our first task is to create a test class in the Checkout basket specifications project this class will inherit from the BddBase class. All the following code was created in the “createbasket” branch of the repository.
public class When_calling_the_basket_api_without_an_id : BddBase
{
protected override void Given()
{
}
protected override void When()
{
}
}
[Test]
public void Then_a_new_empty_basket_is_returned()
{
}
[Test]
public void Then_a_new_empty_basket_is_returned()
{
_basket
}
Now our code will not compile, because we do not have a declaration for the “_basket” field, lets add that.
private Basket _basket;
adding this declaration will require the creation of a “Basket” class, in order that we continue progressing at this point I would suggest simply creating the class in the same file as we are currently working in.
internal class Basket
{
}
[Test]
public void Then_a_new_empty_basket_is_returned()
{
_basket.Items
}
internal class Basket
{
public IList<Object> Items { get; set; }
}
[Test]
public void Then_a_new_empty_basket_is_returned()
{
_basket.Items.Any().ShouldBe(false);
}
protected override void Given()
{
var bootstrapper = new DefaultNancyBootstrapper();
_browser = new Browser(bootstrapper);
}
protected override void When()
{
var response = _browser.Get("/basket");
}
protected override void When()
{
var response = _browser.Get("/basket");
_basket = response.Body.DeserializeJson<Basket>();
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Nancy;
using Nancy.Testing;
using NUnit.Framework;
using Shouldly;
namespace CheckoutBasket.Specifications
{
public class When_calling_the_basket_api_without_an_id : BddBase
{
private Basket _basket;
private Browser _browser;
protected override void Given()
{
var bootstrapper = new DefaultNancyBootstrapper();
_browser = new Browser(bootstrapper);
}
protected override void When()
{
var response = _browser.Get("/basket");
_basket = response.Body.DeserializeJson<Basket>();
}
[Test]
public void Then_a_new_empty_basket_is_returned()
{
_basket.Items.Any().ShouldBe(false);
}
}
internal class Basket
{
public IList<Object> Items { get; set; }
}
}
It is during this phase that a lot of us being to make our mistakes, we start to worry about design, and clean code and all the rest of the very good practices that we will be worrying about in the future, but to worry about them now, and attempt to implement them is a mistake.
I cannot over emphasise this enough, we need to get the test passing as quickly as possible,
At this point in the process we are going to break every rule of coding, cutting and pasting code, copying from the internet, stealing from other projects do what ever you have to, make the test pass, and do it quickly.
Ward Cunningham put is best when he said do the simplest thing that can possibly work, simplicity is the shortest path to a solution, notice he didn’t say best, or well engineered, its the shortest which at this point is what we want, we need our code to work, for the test to be green and for us to be able to move on as quickly as possible. You have permission to do your worst break the rules, and make the test pass.
For us we need a handler for the basket get method, so lets quickly put this together, add a new class to the “CheckoutBasket.API” project.
Nancy uses the constructor of the class to setup routing for a method.
using Nancy;
namespace CheckoutBasket.API
{
public class BasketHander : NancyModule
{
public BasketHander()
{
Get["/basket"] = _ =>
{
return null;
};
}
}
}
using System.Collections.Generic;
using Nancy;
namespace CheckoutBasket.API
{
public class BasketHander : NancyModule
{
public BasketHander()
{
Get["/basket"] = _ =>
{
return Response.AsJson(new Basket() {Items = new List<object>()});
};
}
}
}
public class Basket
{
public IList<object> Items { get; set; }
public bool IsEmpty { get { return ! Items.Any(); } }
}
[Test]
public void Then_an_empty_basket_is_returned()
{
_basket.IsEmpty.ShouldBe(true);
}
Now looking at the handler we are returning a new basket which is correct but should be also be doing the initialization of the items collection in the handler as well, lets move it to the constructor of the basket, stopping the handler knowing about the internals of the basket.
public class BasketHander : NancyModule
{
public BasketHander()
{
Get["/basket"] = _ =>
{
return Response.AsJson(new Basket());
};
}
}
We can now also make the items property private as no one else accesses it.
public class Basket
{
public Basket()
{
Items = new List<object>();
}
private IList<object> Items { get; set; }
public bool IsEmpty { get { return ! Items.Any(); } }
}
public class Basket
{
private IList<object> _items;
public Basket()
{
_items = new List<object>();
}
public bool IsEmpty { get { return ! _items.Any(); } }
}
With that I think we are just about done, the end of the initial test first development phase, notice how we took time over the refactoring,.
We should have taken as much time over the naming of our tests as this helps with designing the API, the only phase we rushed to get through was the getting to green, where we broke all the rules and “Just made it work”.
Once green we went back and applied all the laws of good coding practice, refactoring the code to make it maintainable and easy to read.
All through the process we aimed to keep the code compiling, and the tests running, even while building the very first test, then once failing we got to green as quickly as possible. again keeping the code compiling, only at this last stage do we apply the rules of software design, and because our tests are on the outside looking in, we are able to refactor the internals of our application without breaking tests. Next up will be the id and some thoughts on storage.