Wednesday, March 16, 2011

Solving Kata 16 - Business Rules using BDD - Part 1

The Business Rule coding Kata (Kata Sixteen) deals with a set of changing business rules with an example of writing an order processing application for a large company. The focus is on the payments and in particular on the processing required when a payment was received by the company. The Kata specifies a full set of rule snippets like:
·         If the payment is for a physical product, generate a packing slip for shipping.
·         If the payment is for a book, create a duplicate packing slip for the royalty department.
·         If the payment is for a membership, activate that membership.
·         If the payment is an upgrade to a membership, apply the upgrade.
·         If the payment is for a membership or upgrade, e-mail the owner and inform them of the activation/upgrade.
·         If the payment is for the video "Learning to Ski," add a free "First Aid" video to the packing slip (the result of a court decision in 1997).
·         If the payment is for a physical product or a book, generate a commission payment to the agent.
·         and so on, and so on, for seven long, yellow pages.
We’ll try to solve these rule sets one by one and do this using BDD. I’ll be using the StoryQ framework for writing my BDD test cases.
Part 1: If the payment is for a physical product, generate a packing slip for shipping.
I have created my test case as given below.
[TestMethod]
public void PaymentForAPhysicalProductGeneratesAPackagingSlipForShippingTest()
{
    new Story("payment is for a physical product, generate a packing slip for shipping")
    .InOrderTo("Ship products a packaging slip should be generated")
    .AsA("User")
    .IWant("Packaging slip for shipping my products")
    .WithScenario("Payment for physical product")
    .Given(ProductIsReadyForPayment)
    .When(PaymentIsDoneForProduct)
    .Then(ShouldAlsoGenerateAShippingSlipForTheProduct)
    .ExecuteWithReport(MethodBase.GetCurrentMethod());
}
Now to implement the Given, When, Then scenarios, I’ve use refactoring to generate the methods and variables. My test methods look like.
private void ShouldAlsoGenerateAShippingSlipForTheProduct()
{
    _payment.CompletePayment(PaymentAmount);
    _shippingServiceMock.Verify((x) => x.GenerateShippingSlipForAddress(_physicalProduct.GetShippingAddress()));
}

private void PaymentIsDoneForProduct()
{
    _payment = new Payment(_physicalProduct, ShippingAddress);
    _shippingServiceMock = new Mock<IShippingSlipService>();

    _payment.SetShippingService(_shippingServiceMock.Object);
}

private void ProductIsReadyForPayment()
{
    _physicalProduct = new Product("Blackberry Storm");
    var physicalProduct = _physicalProduct;
    const string shippingAddress = "iSense Bangalore";
    physicalProduct.SetShippingAddress(shippingAddress);
}

I have mocked the IShippingSlipService and set the expectations on the GenerateShippingSlipForAddress method on the service.
Code for the implementation
public class Product
{
    private string _shippingAddress;
    private string _name;
    private readonly string _productCode;

    public Product(string name)
    {
        this._name = name;
        _productCode = Guid.NewGuid().ToString("N");
    }

    public void SetShippingAddress(string shippingAddress)
    {
        this._shippingAddress = shippingAddress;
    }

    public string GetShippingAddress()
    {
        return _shippingAddress;
    }

    public string GetProductCode()
    {
        return _productCode;
    }
}

public class Payment
{
    private readonly Product _physicalProduct;
    private decimal _amount;
    private IShippingSlipService _shippingSlipService;
    private string _shippingAddress;

    public Payment(Product physicalProduct, string shippingAddress)
    {
        if (physicalProduct == null) throw new ArgumentNullException("physicalProduct");
        if (shippingAddress == null) throw new ArgumentNullException("shippingAddress");

        this._physicalProduct = physicalProduct;
        this._shippingAddress = shippingAddress;
    }

    public virtual void CompletePayment(decimal amount)
    {
        _amount = amount;
        if (_physicalProduct != null) _shippingSlipService.GenerateShippingSlipForAddress(_physicalProduct.GetShippingAddress());
    }

    public void SetShippingService(IShippingSlipService shippingSlipService)
    {
        _shippingSlipService = shippingSlipService;
    }
}

public interface IShippingSlipService
{
    void GenerateShippingSlipForAddress(string shippingAddress);
}

Results

Sunday, March 13, 2011

An introduction to BDD using the StoryQ framework

BDD is a higher level of unit testing by using terminology focused on the behavioral aspects of the system rather than testing, it creates better documentation of your system by recording its intent which makes your system easier to learn for new developers and relearn for when you revisit your code further down the line. BDD is a practice that has evolved out of established agile practices and is designed to make them more accessible and effective for teams new to agile software delivery. Over time, BDD has grown to encompass the wider picture of agile analysis and automated acceptance testing.
BDD lets you to write test in the concept of Given-when-then style. For e.g. a normal BDD test case looks like.

new Story("Items can have unit price")
    .InOrderTo("Supermarket can have items with unit prices")
    .AsA("User")
    .IWant("To idenify items with unit prices in the supermarket and get the prices from the checkout list")
    .WithScenario("Items in supermarket will have unit prices")
    .Given(AnItemIsCreated)
    .And(UnitPricingStrategyIsAssignedForTheItem)
    .When(GetPriceMethodIsCalledOnTheItem)
    .Then(ShouldReturnTheUnitPriceForTheItem)
    .ExecuteWithReport(MethodBase.GetCurrentMethod());

This is as simple as converting a user story defined in a template like
·         As a (X)
·         I want to (Y)
·         So that (Z) where Y is some feature, Z is the benefit or value of the feature, and X is the person (or role) that will benefit.
To
·         Given (X)
·         When (Y)
·         Then (Z) where X is some given context, Y is the event or action occurred in the context and Z ensures some outcome.
Frameworks for BDD?
In this post we’ll see how to do BDD using the StoryQ framework. StoryQ  is a portable (single dll), embedded BDD framework for .NET. It runs within your existing test runner and helps produce human-friendly test output (html or text). StoryQ's fluent interface adds strong typing, intellisense and documentation to your BDD grammar. We’ll convert our sample for supermarket pricing coding kata to BDD test cases using the StoryQ framework.
The tests to check whether items have a name and unique number is written as

[TestMethod]
public void ItemTestMethod()
{
    new Story("Items in the supermarket should have a name")
        .InOrderTo("Identity items at checkout")
        .AsA("User")
        .IWant("To identify the items in my checkoutlist")
        .WithScenario("Adding items")
        .Given(ItemHasANameDefined)
        .When(GetNameIsCalled)
        .Then(ShouldReturnTheNameofTheItem)
        .ExecuteWithReport(MethodBase.GetCurrentMethod());
}

[TestMethod]
public void ItemNumberTest()
{
    new Story("Items in the supermarket should have a unique number that is system generated")
    .InOrderTo("Uniquely identify items in the supermarket")
    .AsA("User")
    .IWant("Uniquely identify items in the supermarket")
    .WithScenario("Adding items")
    .Given(ItemIsCreated)
    .When(GetItemNumberIsCalled)
    .Then(ShouldReturnANumberThatIsAutogenerated)
    .ExecuteWithReport(MethodBase.GetCurrentMethod());
}

Then the output of the testrunner will be a report generated as


Tuesday, March 8, 2011

Solving the supermarket coding kata using TDD – Part 3

The supermarket coding kata problem domain deals with the scenario of pricing goods at supermarkets. The scenario handles situations like.
Items in supermarket have simple prices, e.g. One Gatorade for $4.5. Other items may have complex prices like three for dollar or $3 for one pound etc.
This is the last in the 3 part series of our implementation of the above mentioned problem. We’ll be implementing the checkout list and associated test cases.
The checkout list contains item details with the item and the quantity for each item purchased. The total cost of the checkout list is calculated by aggregating the cost of each item details in the list.
[TestMethod]
public void checkoutlist_contains_of_multiple_itemdetails_for_each_item_and_quantity_of_purchased()
{
    CheckoutList checkoutList = new CheckoutList();

    Item kellogs = new Item("Kellogs corn flakes");
    UnitPricingStrategy kellogsPricingStrategy = new UnitPricingStrategy(3.5M);
    kellogs.SetPricingStrategy(kellogsPricingStrategy);

    ItemDetail itemDetail = new ItemDetail(kellogs, 3);

    checkoutList.Add(itemDetail);
    decimal totalPrice = checkoutList.GetTotalPrice();
    Assert.AreEqual(totalPrice, kellogs.GetPrice() * 3);
}

Implementation
public class ItemDetail
{
    private Item _item;
    private int _quantity;

    public ItemDetail(Item item, int quantity)
    {
        this._item = item;
        this._quantity = quantity;
    }

    public decimal GetCost()
    {
        return _item.GetPrice() * _quantity;
    }
}

public class CheckoutList
{
    public CheckoutList()
    {
        _itemDetails = new List<ItemDetail>();
    }

    public void Add(ItemDetail itemDetail)
    {
        _itemDetails.Add(itemDetail);
    }

    List<ItemDetail> _itemDetails;

    public decimal GetTotalPrice()
    {
        decimal totalPrice = 0.0M;
        foreach (var itemDetail in _itemDetails)
            totalPrice += itemDetail.GetCost();

        return totalPrice;
    }
}

Sunday, March 6, 2011

Solving the supermarket coding kata using TDD – Part 2

The supermarket coding kata problem domain deals with the scenario of pricing goods at supermarkets. The scenario handles situations like.
Items in supermarket have simple prices, e.g. One Gatorade for $4.5. Other items may have complex prices like three for dollar or $3 for one pound etc.
In the previous post we’ve implemented the Item class and the tests for testing the functionality of the Item object. We’ll continue working on the sample and now try to implement the different pricing strategies on the items.
·         Create a new class in the test project and name it CountBasedPricingStrategy_Fixture
·         Add the test method to check the functionality of calculating the cost of items that have the pricing strategy based in number of items. E.g. Three for a dollar.
[TestMethod]
public void PricingStrategy_based_on_items_count_should_return_unitprice_based_on_the_parameters_passed()
{
    int itemCount = 3;
    decimal totalPrice = 1.00M;
    PricingStrategy countBasedPricingStrategy = new CountBasedPricingStrategy(itemCount, totalPrice);

    Item item = new Item("Original Glazed Donuts");
    item.SetPricingStrategy(countBasedPricingStrategy);
    decimal itemPrice = item.GetPrice();

    decimal expectedPrice = totalPrice / itemCount;
    Assert.AreEqual(expectedPrice, itemPrice);
}

·         As in the previous steps use the refactoring tool to generate the new class and the related methods. Run the test case and watch it fail.
·         Now implement the methods with the required functionality to make this test case pass.
public class CountBasedPricingStrategy : PricingStrategy
{
    private int _itemCount;
    private decimal _totalPrice;

    public CountBasedPricingStrategy(int itemCount, decimal totalPrice)
    {
        this._itemCount = itemCount;
        this._totalPrice = totalPrice;
    }

    public override decimal GetUnitPrice()
    {
        return _totalPrice / _itemCount;
    }
}

public abstract class PricingStrategy
{
    public abstract decimal GetUnitPrice();
}

·         As you can see I have abstracted the pricing calculation strategy to an abstract class and implemented the new class from the abstract class.
·         Similarly we can implement the other pricing strategies as given below.
[TestClass]
public class UnitPricingStrategy_Fixture
{
    [TestMethod]
    public void unit_pricing_strategy_returns_the_unitprice_for_the_item()
    {
        decimal unitPrice = 50M;
        UnitPricingStrategy unitPricingStrategy = new UnitPricingStrategy(unitPrice);

        Item item = new Item("Kellogs corn flakes");
        item.SetPricingStrategy(unitPricingStrategy);

        Assert.AreEqual(unitPrice, item.GetPrice());
    }
}

public class UnitPricingStrategy : PricingStrategy
{
    private decimal _unitPrice;

    public UnitPricingStrategy(decimal unitPrice)
    {
        this._unitPrice = unitPrice;
    }

    public override decimal GetUnitPrice()
    {
        return _unitPrice;
    }
}

[TestClass]
public class WeightBasedPricingStrategy_Fixture
{
    [TestMethod]
    public void items_price_should_differ_based_on_the_packed_weight()
    {
        decimal totalPrice = 99.99M;
        decimal baseWeight = 2M;

        decimal packetWeight = .5M;
        PackedWeightPricingStrategy priceWeightPricingStrategy = new PackedWeightPricingStrategy(totalPrice, baseWeight, packetWeight);

        Item item = new Item("Apple juice 2Ltr");
        item.SetPricingStrategy(priceWeightPricingStrategy);

        decimal expectedPrice = (totalPrice / baseWeight) * packetWeight;
        Assert.AreEqual(expectedPrice, item.GetPrice());
    }
}

public class PackedWeightPricingStrategy : PricingStrategy
{
    private decimal _totalPrice;
    private decimal _baseWeight;
    private decimal _packetWeight;

    public PackedWeightPricingStrategy(decimal totalPrice, decimal baseWeight, decimal packetWeight)
    {
        this._totalPrice = totalPrice;
        this._baseWeight = baseWeight;
        this._packetWeight = packetWeight;
    }

    public override decimal GetUnitPrice()
    {
        return (_totalPrice / _baseWeight) * _packetWeight;
    }
}

Next we’ll implement the checkout list and complete the first coding kata sample. Till then happy coding…