Test Driven Design using mocks- Lessons Learnt (Part 2)


This is a follow up to my previous post. In this somewhat contrived example we are building a point of sale system and there is a requirement to print out a receipt for a sale, which includes item details, total taxes and the total amount due.
Using Rinho mocks Here is the first test I wrote initially, which drove out the need
for a Renderer role, whose responsibility is to render the contents of the receipt
onto an output device

[TestFixture]
public class SalesTests
{
private MockRepository mockery;

[SetUp]
public void BeforeTest()
{
mockery = new MockRepository();
}

[TearDown]
public void AfterTest()
{
mockery.VerifyAll();
}

[Test]
public void ShouldbeAbleToPrintReceiptForASaleWithNoItems()
{
var sale = new Sale();
var renderer = mockery.StrictMock<IRender>();
renderer.Render(”Total Taxes: 0.00 \n Total: 0.00″);
mockery.ReplayAll();
sale.PrintReceiptOn(renderer);

}
}
the implementation needed to pass the above test was as follows

public interface Renderer{
void render(String s);
}
public class Sale {

public void PrintReceiptOn(IRender renderer)
{
renderer.Render(”Total Taxes: 0.00 \n Total: 0.00″);
}

By driving the design test first I was able to discover the need for an object to play the Renderer role, using mocks I was able to drive out interface for the Renderer role.
But I also realised later the above test was brittle and over specified, because
the test was very tightly coupled to the implementation, because the conversation between the objects was not at the right level.
for instance if I changed the implementation of printReceiptOn on the Sale object to

public void PrintReceiptOn(IRender renderer)
{

renderer.Render(”Total Taxes: 0.00″);
renderer.Render(”Total: “);
renderer.Render(”0.00″);
}

The test will break (because the test specifies the Render message be sent to the renderer only once with a specific argument) although the implementation is
quite valid. So the test is tightly coupled to the implementation
Now we could try addressing the symptom by defining a looser constraint on the test as shown below

[Test]
public void ShouldbeAbleToPrintReceiptForASaleWithNoItems()
{
var sale = new Sale();
var renderer = mockery.StrictMock<IRender>();
renderer.Render(”Total: 0.00″);
LastCall.Repeat.AtLeastOnce().IgnoreArguments();
mockery.ReplayAll();
sale.PrintReceiptOn(renderer);

With the above either of my previous implementation would make the
test pass. however it doesn’t test the right data was being rendered.

To ensure that the right data was being rendered
We could write a state-based using a collecting stub to verify the data being
rendered is correct as shown below
[Test]

public void VerifyReceiptContentsForSaleWithNoItems() {
TestRenderer renderingDevice = new TestRenderer();
sale.printReceiptOn(renderingDevice);
Assert.assertEquals(”Total Taxes: 0.00 \n Total: 0.00″,
renderingDevice.GetContents());

}

where TestRenderer is a collecting stub which is implemented as follows

public class TestRenderer : IRenderer {
private String contents;

public void Render(String s) {
contents += s;
}
public String GetContents() {
return contents;
}
}
At this point I was questioning the usefulness of a mock, it seemed I was better off using a state based test and using a colleting stub to verify. The brittleness of our initial interaction based test, indicated an underlying design issue, interaction was not specified at the right level. The trick here is to design interactions that are meaningful to the calling object, this leads to interface discovery, (and this is one of the reasons why you should not mock 3rd party API’s). Now Render(string s) is not a meaningful message to the Sale object to be sending to the Renderer, the Sale object knows about items, taxes and total so What would have been more a meaningful interaction (conversation ) between the Sale object and Renderer would be to specify the interaction in terms of taxes, items and total, as shown below.

[Test]
public void ShouldbeAbleToPrintReceiptOnAPrinterForSaleWithNoItems()
{
renderer.RenderTaxes(0.0m);
renderer.RenderTotal(0.0m);
mockery.ReplayAll();
saleUnderTest.PrintReceiptOn(printer);
}
public interface Renderer {
void RenderTotal(decimal total);
void RenderTaxes(decimal taxes);
}

the following is now the implementation of the PrintReceiptOn method of the Sale Object

public void PrintReceiptOn(IRender renderer)
{

renderer.RenderTaxes(totalTaxes);
renderer.RenderTotal(total);
}
This is where mock objects are so useful as they aid in the discovery of interfaces and quickly give us feedback in the form of brittle tests when interactions between objects are poorly designed.

About these ads

4 Comments on “Test Driven Design using mocks- Lessons Learnt (Part 2)”

  1. […] between objects an abstract level”. I will try explaining by giving an example in the next post Possibly related posts: (automatically generated)TDD […]

  2. That’s a great article! It really shows the thought process of how TDD works, and how it can lead to better design.

    Although now I’m a bit curious about the process that motivated you to create a Sale#PrintReceiptOn(Renderer) method rather than creating a Renderer#PrintSale(Sale) method. Have you talked about that type of design decision in any of your other blog posts?

  3. isaiahperumalla says:

    @Aaron
    Thanks, thats a good question. this example was simply to demonstrate TDD using mocks.
    An alternate solution would be Renderer#PrintSale(Sale), this probably makes more sense depending on the domain. I will write a post on some of the alternatives and the refactoring the interaction model.

    you might find this article useful http://msdn.microsoft.com/en-ca/magazine/dd882516.aspx

  4. Pedro says:

    Actually, putting it like

    Sale#PrintReceiptOn(Renderer)

    makes more sense, if both taxes and item subtotals belong to the Sale class (which is the case, if I’m right). This way, we’re delegating the rendering responsibility to another object, without breaking encapsulation. The latter would happen if the call were made like Renderer#PrintSale(Sale); the renderer would need to ask the sale about its data in order to render something. Remember, “Tell, Don’t Ask” ;)

    Cheers.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.