CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeremy D. Miller -- The Shade Tree Developer

Under the hood and working with .Net, TDD, Software Design, and Agile Stuff

TDD Design Starter Kit: It’s All about Assigning Responsibilities

In a previous post, TDD Design Starter Kit - State vs. Interaction Testing, I used a hugely contrived sample user story to illustrate the difference between state and interaction testing.  I’d like to revisit that story and talk about how to design a class structure for easier testability.  Here’s a rehash of the user story:

 

Email Alert for Invoice Validation Failures User Story

  • When an invoice is being processed, if certain nebulous validation failure conditions are met, send an email to the accounts payable individual assigned to the invoice. The email should list all of the invoice errors in a certain html format.
  • If the invoice is really, really bad, send a summary email to the proper expediter. The email message will be html formatted in a particular manner.
  • If the invoice is successfully validated, publish the invoice (via MSMQ)

Interaction with a database, reading user information from Active Directory, and sending email messages are all required to implement this feature.  This is exactly the kind of system that can be difficult to build with Test Driven Development, especially for those new to TDD.  I know that I struggled at first while unit testing code that depended on external services.  These things are generally harder to test than code that runs inside a single process.

 

Just Use Mock Objects, Right?

 

We know that we can use mock objects to test the interaction of our code with the external resources.  That enables us to take most of the system infrastructure out of the equation for unit testing, but it comes at a cost.  Mock objects are not a “cure all” for unit test woes.  Setting up mock objects is coding overhead and can make a unit test harder to understand and debug.  Excessive mocking also leads to unit test code that is tightly coupled to the actual implementation of the code it is testing.  Automated unit tests are supposed to give you a sense of confidence when you’re making changes.  Being afraid of making changes in your code because you’ll break unit tests is a major design smell. 

 

“Bottom Up” Construction

 

Mock objects are an important part of the answer, but not the entire answer.  What we want to do is create a class structure that makes unit testing as easy as possible.  That means that our unit tests should be simple to setup, isolated, fine-grained, and reveal the intention of the code being tested.  Ideally, we want to focus first on creating one piece of functionality at a time, then worry about assembling the whole from the little parts later. 

 

Oftentimes the hardest part of doing any feature with TDD is simply getting started with the first test.  To draw an analogy from physics, the friction between any two surfaces is usually much greater at rest than it is when the surfaces are already moving relative to one another.  One of the ways to break the initial friction and get some momentum is to pick off an easily understandable chunk of the work, ignore the rest of the feature, and just do it.  You can worry about assembling the working chunks together later.  Moreover, you can rearrange the little pieces in different ways as the final class structure emerges. 

 

Single Responsibility Principle (SRP)

 

The obvious objection to this coding style is the fear of bogging down with rework if you have to change the classes you’ve already created to fit the emerging design.  The answer to this very reasonable objection is to religiously follow the Single Responsibility Principle (this is synonymous with the concept of class cohesion).

 

This principle is stated as:

 

            “There should never be more than one reason for a class to change.”

 

Or:

 

“One class, one responsibility.” 

 

A quick example from the email alert story is the creation of the HTML body of an email.  Creating the HTML report might be an easy place to start with this story.  We can start by creating a class whose only responsibility is to take in an array of invoice objects with the associated invoice error objects and create an HTML-formatted string.  This class won’t need to go to the database to fetch data or manipulate any kind of external resource.  Everything it needs to perform its work is handed to it by something else.  Here’s a skeleton of this class with an associated unit test.

 

	public class MessageBodyCreator
{
public string CreateSummaryBody(InvoiceErrorReport report)
{
// create the HTML
return "";
}

public string CreateDetailBody(InvoiceErrorReport report)
{
// create the HTML
return "";
}
}

[TestFixture]
public class MessageBodyCreatorTester
{
[Test]
public void CreateABasicSummaryBody()
{
InvoiceErrorReport errorReport = createBasicErrorReport();
string expectedHTML = "blah,blah,blah";

MessageBodyCreator creator = new MessageBodyCreator();
string actualHTML = creator.CreateSummaryBody(errorReport);

Assert.AreEqual(expectedHTML, actualHTML);
}
}

 

Remember, by far the simplest unit test to write and read is a state-based unit test where you merely push known input data in and check the expected results coming out.  The MessageBodyCreator class simply takes in an InvoiceErrorReport object and returns a string representing the HTML report.  All the information it needs to do its job is given to MessageBodyCreator in the method calls.  MessageBodyCreator satisfies the Single Responsibility Principle because it won’t have to be changed later when we start to add code for MSMQ support or the invoice processing validation. 

 

Responsibility Driven Design

 

The structure of an OO application is largely determined by how you assign responsibilities to the classes in the application.  Like modeling business domains with OO, there is not going to be one single way to define the class responsibilities of a system.

Fortunately for us, there is an entire body of work dealing with defining and assigning class responsibilities called Responsibility Driven Design (RDD) that we can look to for guidance on this very subject.

RDD defines a class responsibility as “an obligation to perform a task or know information.”  To be more specific, there are three basic categories of responsibilities:

  1. Know things
  2. Do things
  3. Make decisions

“Know things” is a pretty obvious reference to data access classes and other sources of information like configuration files and “Request/Reply” style web services.  The implication of the second and third categories is more interesting.  It is very often helpful to test an action and the decision to take regarding that action separately.  In the case of the email alert story, we probably want to create separately unit tested classes for the actual creation of the invoice summary email, and the decision to create and send the invoice summary email.  By separating the unit testing we create much simpler unit tests by reducing the number of variables in each unit test.  Test small before you test big.

 

Taking another look at the email alert story, let’s list out the primary responsibilities.

  • Validating and verifying an invoice (Do things, Make decisions).  This is most likely an entire subsystem of the application.
  • Creating the HTML detail report of the invoice errors (Do things)
  • Creating the HTML summary report of the invoice errors (Do things)
  • Looking up the proper expediter for the invoice and the accounts payable personnel (Know things)
  • Building an email message (Do things)
  • Sending an email via SMTP (Do things)
  • Putting a message on the MSMQ queue (Do things)
  • Deciding what action to take on an invoice (Make decisions)

So now we need to assign the responsibilities to the classes we’re going to create to fulfill the user story.  To help us create candidates for classes to implement these responsibilities, let’s examine another concept from Responsibility Driven Design.

 

Object Role Stereotypes

 

From Rebecca Wirfs-Brock, “Stereotypes are simplified views that help you understand an object or component’s purpose.”  Every class fulfills at least one stereotype and possibly multiple stereotypes. 

Here are five object role stereotypes that are pertinent to the email alert story.

 

  1. Controller – Controls application execution.  Makes decisions and delegates to other classes.
  2. Service Provider – Provides a service to other classes.  Performs a calculation, computation, or executes business rules.
  3. Interfacer – Communicates actions to other layers or systems.
  4. Information Holder – Holds facts.  Domain classes.
  5. Structurer – Maintains relationships between other classes

 

Let’s start with some of the easier roles.  We’ll need an Information Holder class to represent an Invoice.  We also need to store the errors on each invoice.  We might decide to keep this information on the Invoice class itself, but I built the sample code with a Structurer class called InvoiceErrorReport that will create the linkage between invoices details and the resulting validation errors.

 

One of my primary rules in TDD is to “isolate the ugly.”  By this I mean that you take any thing that is inconvenient during unit testing like database access and isolate this functionality behind an abstracted wrapper interface.  Using this philosophy, I would suggest Interfacer classes for the SMTP, MSMQ, and user information store interaction.  Here are the interface names I came up with:

 

    • IMessagingGateway
    • IEmailGateway
    • IUserInformationStore

 

These classes will definitely be mocked in unit tests, so I’ll concentrate on the abstracted interfaces first.  The structure of these interfaces will be defined by the needs of the calling clients, so my preference would be to ignore the implementation of these interfaces until late in the story after the interface signatures are established. 

 

Next, we need Service Provider classes to perform the verification of invoices, create the HTML invoice error message, and create the email messages.  That leads to these classes:

 

  • IInvoiceValidator – Responsible for all the verification and validation of the invoice.  Creates an InvoiceErrorReport for an invoice.
  • MessageBodyCreator – Creates HTML reports for a given InvoiceErrorReport.
  • INotificationService/NotificationService – Creates and sends and email message for an InvoiceErrorReport.  Collaborates with the IEmailGateway, MessageBodyCreator, and IUserInformationStore classes.

 

Lastly, we need some kind of Controller class to coordinate the interactions between all of the other classes.  The InvoiceProcessor class will be responsible for deciding to send emails or forward the invoice to the messaging system.  The InvoiceProcessor will interact with IInvoiceValidator, INotificationService, and IMessagingGateway.

 

The Skeleton Code

 

Here finally is a skeleton of this class structure.  Most of the class interaction is in the InvoiceProcessor class.  When we unit test the InvoiceProcessor class we’ll use mock objects for the other service provider classes.  In this case we’re specifically concerned with testing the coordination and interaction with the other classes.  In the case of the MessageBodyCreator and the IInvoiceValidator classes, we don’t need to mock anything in the unit tests.  That’s an important point to consider because it’s likely that these classes will require far more unit test scenarios to fully implement the user story.

 

	public class InvoiceProcessor
{
// "Dependency Inversion Principle" -- All dependencies are now to an abstracted interface
private readonly IInvoiceValidator _validator;
private readonly IMessagingGateway _messagingGateway;
private readonly INotificationService _notificationService;



// Use "Constructor Injection" to push dependencies into InvoiceProcessor
public InvoiceProcessor(IInvoiceValidator validator, IMessagingGateway messagingGateway, INotificationService notificationService)
{
_validator = validator;
_messagingGateway = messagingGateway;
_notificationService = notificationService;
}


public void ProcessInvoices(Invoice[] invoices)
{
InvoiceErrorReport errorReport = _validator.ValidateInvoices(invoices);

// determine if the critical error threshold has been exceeded
if (errorReport.HasTooManyCriticalErrors())
{
_notificationService.SendSummaryMessage(errorReport);
}
// determine if the warning threshold for the error count has been reached
else if (errorReport.HasTooManyErrors())
{
_notificationService.SendDetailMessage(errorReport);
}
else
{
// no longer responsible for knowing where the MSMQ path is. Or even if this
// is an MSMQ
_messagingGateway.SendInvoices(invoices);
}
}

// Other private methods
}


public interface IInvoiceValidator
{
InvoiceErrorReport ValidateInvoices(Invoice[] invoices);
}


public interface INotificationService
{
void SendSummaryMessage(InvoiceErrorReport report);
void SendDetailMessage(InvoiceErrorReport report);
}


public interface IMessagingGateway
{
public void SendInvoices(Invoice[] invoices);
}


public class NotificationService : INotificationService
{
private readonly IEmailGateway _emailGateway;
private readonly IUserInformationStore _userStore;
private MessageBodyCreator _messageCreator;

public NotificationService(IEmailGateway emailGateway, IUserInformationStore userStore)
{
_emailGateway = emailGateway;
_userStore = userStore;
_messageCreator = new MessageBodyCreator();
}

public void SendSummaryMessage(InvoiceErrorReport report){}

public void SendDetailMessage(InvoiceErrorReport report){}
}


public class MessageBodyCreator
{
public string CreateSummaryBody(InvoiceErrorReport report){}
public string CreateDetailBody(InvoiceErrorReport report){}
}


// SAMPLE UNIT TEST of InvoiceProcessor

[TestFixture]
public class InvoiceProcessor
{
[Test]
public void SendTheSummaryEmailToTheExpediterIfTheCriticalErrorThresholdIsMet()
{
// 1.) Create the NMock objects
DynamicMock messagingMock = new DynamicMock(typeof(IMessagingGateway));
DynamicMock validatorMock = new DynamicMock(typeof(IInvoiceValidator));
DynamicMock notificationMock = new DynamicMock(typeof(INotificationService));

// Setup the test data
Invoice[] invoices = new Invoice[0];
InvoiceErrorReport report = new InvoiceErrorReport();

// 2.) Setup the expectations. Gets the email addresses, sends an email address, but does not put the invoice array onto
// the outgoing queue
validatorMock.Expect("ValidateInvoices", invoices);
validatorMock.ExpectAndReturn("HasTooManyCriticalErrors", true);
validatorMock.SetupResult("CreateErrorReport", report);

messagingMock.ExpectNoCall("SendInvoices", typeof(Invoice[]));

notificationMock.Expect("SendSummaryMessage", report);

// 3.) Create the InvoiceProcessor object with the Mock instances
InvoiceProcessor processor = new InvoiceProcessor(
(IInvoiceValidator) validatorMock.MockInstance,
(IMessagingGateway) messagingMock.MockInstance,
(INotificationService) notificationMock.MockInstance);

// 4.) Execute
processor.ProcessInvoices(invoices);

// 5.) Verify the correct calls were made
messagingMock.Verify();
validatorMock.Verify();
notificationMock.Verify();
}

}

 

This class structure should also allow our system to evolve as business and technical demands change.  If the CIO decides to standardize on MQSeries instead of MSMQ, we just need to change the IMessagingGateway implementation.  If the HTML format changes (and it will), only the MessageBodyCreator class needs to be modified.

 

Wrapping Up

 

I certainly wouldn’t recommend trying to completely design this entire class structure before writing a single line of code, but Responsibility Driven Design is well suited as a supplement to Test Driven Development.  “Breaking the ice” on a user story and getting that first unit test written can be difficult.  The design strategies behind RDD can give developers a way to get started by opportunistically slicing off code responsibilities that can be constructed separately.  The Single Responsibility Principle helps us to create classes that are more able to be constructed and changed independently of other classes so that we can work in an evolutionary manner of constructing software from the “bottom up” without a lot of rework.

 

If you’re interested in learning about Responsibility Driven Design, I would recommend that you check out Rebecca Wirf-Brock’s website as a start.   



Comments

Jeremy D. Miller -- The Shade Tree Developer said:

I've posted a new article today entitled TDD Design Starter Kit: It’s All about Assigning Responsibilities. ...
# September 6, 2005 11:36 AM

Jeremy D. Miller -- The Shade Tree Developer said:

Inversion of Control (IoC) is an essential tool for any software designer’s design toolbox because it’s...
# September 20, 2005 3:52 PM

Fregas said:

I don't get it.

I don't get all this <a href="http://www.dotnettricks.com/Home/tabid/52/Default.aspx">TDD stuff.</a>
# September 24, 2005 11:17 PM

Jeremy D. Miller -- The Shade Tree Developer said:

In the last post I introduced mock objects and talked about some of the various flavors of fake objects.&amp;nbsp;...
# December 20, 2005 5:23 PM

Jeremy D. Miller -- The Shade Tree Developer said:

In the last post I introduced mock objects and talked about some of the various flavors of fake objects.&amp;nbsp;...
# December 20, 2005 5:28 PM

Jeremy D. Miller -- The Shade Tree Developer said:

In the last post I introduced mock objects and talked about some of the various flavors of fake objects.&amp;nbsp;...
# January 14, 2006 10:23 AM

Jeremy D. Miller -- The Shade Tree Developer said:

Inversion of Control (IoC) is an essential tool for any software designer’s design toolbox because it’s...
# January 18, 2006 4:12 PM

Jeremy D. Miller -- The Shade Tree Developer said:

Jeffrey's post on Rocky Lhotka's off the cuff remarks about TDD is spawning a good discussion and several...
# March 31, 2006 3:52 PM

Jeremy D. Miller -- The Shade Tree Developer said:

Another follow up to &quot;Test Small, Before Testing Big&quot;
We're most productive when we are coding &quot;in the...
# June 2, 2006 2:14 PM

Jeremy D. Miller -- The Shade Tree Developer said:

Between being extremely short handed at work, tech' reviewing a new book, a
possible book proposal...
# August 7, 2006 4:51 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Jeremy D. Miller

Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy previously worked as a systems architect building mission critical supply chain software for a Fortune 100 company and learned agile development practices as a .Net consultant at ThoughtWorks, one of the pioneers of agile development. Jeremy is the author of the open source StructureMap (http://structuremap.sourceforge.net) tool for Dependency Injection with .Net and the forthcoming StoryTeller (http://storyteller.tigris.org) tool for supercharged FIT testing in .Net. Jeremy's thoughts on just about everything software related can be found on his weblog "The Shade Tree Developer" at http://codebetter.com/blogs/jeremy.miller, part of the popular CodeBetter site. Jeremy is a Microsoft MVP for C#. Check out Devlicio.us!

Our Sponsors

Proudly Partnered With


This Blog

Syndication

News

All opinions expressed here constitute my (Jeremy D. Miller's) personal opinion, and do not necessarily represent the opinion of any other organization or person, including (but not limited to) my fellow employees, my employer, its clients or their agents.

About Me

"Best Of" Compendium

StructureMap (Dependency Injection for .Net)

StoryTeller (Supercharged Fit)

Build your own Cab

TestDriven

MVP