Saturday, April 11, 2009

NCharlie: Test Driven Development – part 1

This is the second post in a series that tracks my progress implementing some of the technologies, patterns and methods I picked up at an Agile .Net boot-camp in March 2009.

The exercise for this post is to strip down my sandbox project, NCharlie, to a point where I can really see the benefit of test-first development.  Then I will add a feature to the project using the process my instructor followed (“User Story Execution Process”, described here).  You can download the source for this exercise by browsing the tdd-demo branch of the NCharlie project on Google Code.

The project is a simple task management tool.  The first feature I’ll implement is the search feature.  Here’s the requirement:

“A user should be able to search for existing tasks.”

I know, that is a pretty vague requirement.  I’ll add on more layers later… for now, this requirement will serve nicely (I hope) in demonstrating the TDD method.  I spent some time setting up my “Core” project which contains the domain model (for now, just one entity… a Task).  Next I create a sequence diagram describing a search for existing tasks:

task-manager-search-sequence

Aside: If you are staring at this diagram wondering why I’m using Views, Controllers and Repositories, it is because of the architecture decisions I’ve made for this project.  I hinted at the motivation for this architecture in the first post of this series… I may come back to that in more detail later.  For now though I will just admit that it was pretty easy to think about the ingredients for this sequence diagram because I had already built this and now I’m just starting over.  When I built NCharlie initially there was some pain I had to work through getting the parts of the Supervising Controller pattern to work in the context of a webforms application using the Onion Architecture.  But that is figured out now, and I won’t have to figure it out again on every application.  I know now that I need to model View events and handle those events in a Controller.  The Repository pattern is used to abstract away the persistence (database) part of the application. (end of Aside)

Back to the exercise… I’ll break the elements of the sequence diagram down into development tasks:

  1. Create interfaces for TaskManagerView and TaskRepository.
  2. Write tests, then write code to raise and handle the SearchRequestEvent method.
  3. Write tests, then implement the Search method.
  4. Write tests, then implement the ShowSearchResults method.

Step 1: Here are the two interfaces:

public interface ITaskManagerView : IView
{
event EventHandler<TaskSearchEventArgs>
SearchRequested;
void ShowSearchResults(ICollection<Task>);
}



public interface ITaskRepository : IRepository<Task>
{
Task[] Search(
TaskSearchSpecification specification);
}



The view interface is derived from the IView interface, which I won’t explain in detail here.  Phil Haack did a good job of explaining this in his Supervising Controller example – IView holds common event handlers and methods for a web form (Init, Load, IsPostBack…)



The repository interface is derived from IRepository, which holds CRUD methods used to persist objects.  I added the Search method to ITaskRepository.



Step 2: The SearchRequestEvent:



For this step, I need to write 2 tests.  First, the Controller must listen (subscribe to) the View event so that it can respond.



[Test]
public void Verify_controller_attaches_to_search_requested_event()...



Second, I want to make sure the Controller calls the Search method on the repository when this event is raised.



[Test]
public void Repository_search_method_should_be_called_in_response_to_search_request()...



You may be asking why I left the guts of these tests out of this post?  After all, this is supposed to be a post about writing tests first.  I struggled with this for a while and decided not to show the implementation here because I don’t want to get sidetracked trying to explain how to create mock calls to the view and to the repository.  One of the benefits of writing these tests is that I can do so before I create any ASPX pages or database procedures.  Before I even have to think about ASPX or database connections and queries I can have a compiling project with unit tests that specify the behavior I want to achieve.  That is the value I hope to convey in this post.  The test implementation details are available in the source for this exercise, and I hope to cover it in more detail in a later post.



After implementing the above two tests correctly my project compiles, but when I execute the tests they fail (I’m using the Resharper test runner to execute my test methods.  At boot-camp we used TestDriven.Net.  You can also execute these tests from NUnit’s console runner or the command line if you like.):



SearchRequestedEvent tests failed



Now I’ll write the code to make these two tests pass.  I create an event handler method (OnSearchRequested) that calls the repository’s Search method and attach it to the View’s SearchRequested event:



public class TaskManagerController
{
private readonly ITaskManagerView _view;
private readonly ITaskRepository _repository;

public TaskManagerController(
ITaskManagerView view,
ITaskRepository repository)
{
this._view = view;
this._repository = repository;
SubscribeViewToEvents();
}

private void SubscribeViewToEvents()
{
_view.SearchRequested += OnSearchRequested;
}

private void OnSearchRequested(
object sender,
TaskSearchEventArgs e)
{
ICollection<Task> tasks =
_repository.Search(e.SearchSpecification);
_view.ShowSearchResults(tasks);
}

}



Now I rebuild and execute my unit tests:



SearchRequestedEvent tests pass



These two unit tests are passing now, and they don’t require an ASPX page or a database to do so.  I think that is valuable!  They clearly describe what the behavior of this part of my application should be and they enforce that behavior without requiring me to think about the specific implementation of the web form or the repository.



Next time I will continue with steps 3 and 4 in my task list above... testing and implementing the Search and ShowSearchResults methods.



<< Development Driven Tests (Introduction), NCharlie: Test Driven Development, part 2 >>

No comments:

Post a Comment