Thursday, 25 October 2012

Simple BDD - Take 2

A while back I wrote the post Simple BDD where the idea was to show how you can write a self documenting, human readable test in BDD style without the need of any fancy frameworks using fluent syntax. I was literally inundated by feedback from one person. The complaint was that it is weird to write statements such as:

Given
Given
When
When
When
Then
Then

The argument is that this doesn't really read like a story and just seems like I am stuttering.

So I would like to make a small modification to this to show 2 points.

The test name

The test name will really be something descriptive that is most logical:

GivenARectangleWithWidthOf10AndHeightOf10TheAreaShouldCalculate100

I am sure I don't have to add the virtual spaces to make it more readable at this point as you would get the idea. The wording here is completely logical.

Implementation

To actually write this implementation that satisfies the test I am going write a Shape and Calculator and throw in a Visitor just to make it more interesting.

public class Shape
{
}
public class Rectangle : Shape
{
public int Width { get; set; }
public int Height { get; set; }
public T Accept<T>(ShapeVisitor<T> visitor)
{
return visitor.Visit(this);
}
}
public abstract class ShapeVisitor<T>
{
public abstract T Visit(Rectangle shape);
}
public class ShapeAreaCalculator : ShapeVisitor<int>
{
public override int Visit(Rectangle shape)
{
return shape.Width * shape.Height;
}
}
view raw Classes.cs hosted with ❤ by GitHub

Now to write the grammar in a little bit more like a human, I thought to simply add an And method.

public TestClass And(TestClass setup)
{
return this;
}
view raw And.cs hosted with ❤ by GitHub
Here is the full test and test class implementation:
[TestClass]
public class TestClass
{
private Rectangle _rectangle;
private ShapeAreaCalculator _calculator;
[TestMethod]
public void GivenARectangleWithWidthOf10AndHeightOf10TheAreaShouldCalculate100()
{
this
.Given(GivenNewRectangle()
.And(GivenShapeAreaCalculator(new ShapeAreaCalculator())))
.When(WhenRectangleWidth(10)
.And(WhenRectangleHeight(10)))
.Then(() => _rectangle.Accept(_calculator) == 100)
.ThenAreaIs(100);
}
public TestClass Given(TestClass when)
{
return this;
}
public TestClass GivenNewRectangle()
{
_rectangle = new Rectangle();
return this;
}
public TestClass GivenShapeAreaCalculator(ShapeAreaCalculator calculator)
{
_calculator = calculator;
return this;
}
public TestClass When(TestClass when)
{
return this;
}
public TestClass WhenRectangleWidth(int n)
{
_rectangle.Width = n;
return this;
}
public TestClass WhenRectangleHeight(int n)
{
_rectangle.Height = n;
return this;
}
public TestClass And(TestClass setup)
{
return this;
}
public TestClass ThenAreaIs(int area)
{
int result = _calculator.Visit(_rectangle);
Assert.AreEqual(result, area);
return this;
}
public TestClass Then(Func<bool> a)
{
Assert.IsTrue(a());
return this;
}
}
view raw TestClass.cs hosted with ❤ by GitHub

Note

I DO recommend that you do not use too many evaluations (Asserts/Then) in one test and to rather break them up. The fact that I have shown it is just an example of what the syntax can do. Also the grammar is not set in stone and the idea is to adapt it to what you prefer.

No comments:

Post a Comment