Saturday, April 18, 2009

NCharlie: TDD – part 2 (an integration test)

In my last post of this series I wrote unit tests to verify that an event (SearchRequested) from the View was handled by the Controller in my Agile/TDD sandbox application NCharlie.  I started with a sequence diagram showing the interactions needed to accomplish a search, then wrote down the tasks needed to complete this feature:

  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.

Two down, two to go.  The first two tasks could be test driven using unit tests.  Rhino Mocks allowed me to create a mock View and a mock Repository and concentrate on the TaskManagerController class… making sure it responded and handled the SearchRequested event correctly.  Now I want to wire up the Search method to return some actual results, and I want to show those results in an ASPX page, like this:

TaskManagerSearch

Since the Search method will be interacting with my database, I’ll need to test it using an integration test.  I like the way CodeCampServer keeps integration tests in a separate project… from the Resharper test runner it is easy to run all the tests in a project – so keeping the fast-executing unit tests separated seems to be a good idea.  I will add a new project to the NCharlie solution for integration tests and get to work (source is available in the tdd-demo branch of the NCharlie project.)

Here is the test method I want to build:

[Test]
public void Should_search_by_task_description()...


To write this test I will need to able to save Task entities to the database, retrieve them using the Search method, and make sure that the returned items contain the correct search results.  The saving part is accomplished in NCharlie using NHibernate and some helper classes that can be found in the open source projects CodeCampServer and Tarantino: IRepository, PersistentObject and HybridSessionBuilder.  These are mostly the same in NCharlie, though I did make a few changes so they would be .Net 2.0-friendly for this project.  The “PersistEntities” method below saves entities to the database, ensures the transaction is committed and the session is closed, so that subsequent calls to the database in this test can count on the entities being there.  I have not shown it here, but when running integration tests against a database it is important to setup the test first and make sure the database is in a consistent state.  This is done via a [SETUP] method in the RepositoryTestBase class, which my TaskRepositoryTester class is derived from.  Here is the test that verifies the search method is working correctly:



[Test]
public void Should_search_by_task_description()
{
Task task1 = new Task("foo");
Task task2 = new Task("xFOo1");
Task task3 = new Task("no fu");

PersistEntities(task1, task2, task3);

TaskSearchSpecification searchSpecification =
new TaskSearchSpecification();
searchSpecification.Description = "foo";

ITaskRepository repository =
new TaskRepository(new HybridSessionBuilder());
Task[] tasks = repository.Search(searchSpecification);

CollectionAssert.Contains(tasks, task1);
CollectionAssert.Contains(tasks, task2);
CollectionAssert.DoesNotContain(tasks, task3);
}


The “TaskSearchSpecification” class contains the search input fields (in this example I’m only searching on the Description field.)  Finally, I call the actual “Search” method on the TaskRepository and verify the results using the “CollectionAssert” class (part of NUnit.)



This test fails initially, and I implement the search method in TaskRepository to make it pass:



public Task[] Search(TaskSearchSpecification specification)
{
ICriteria criteria = GetSession()
.CreateCriteria(typeof (Task));

IList<Task> tasks = criteria
.Add(Restrictions
.InsensitiveLike("Description",
specification.Description,
MatchMode.Anywhere)
)
.List<Task>();

Task[] taskArray = new Task[tasks.Count];
tasks.CopyTo(taskArray, 0);

return taskArray;
}



task-search-test


For more information:



As I was writing this post I ran across Billy McCafferty’s article on NHibernate Best Practices with ASP.Net… I highly recommend it if you are interested in how these patterns are implemented.  I know things have changed in our field since he wrote this article (Billy later contributed the S#arp architecture which uses ASP.Net MVC), but this article is a great resource… especially for someone like me who works in a place where Webforms are still the tool we use to get our work done.  It is also very apparent from reading Billy’s article just how much it has influenced the ASP.Net world since it was written.  The CodeCampServer project I’m basing NCharlie on uses much of the architecture and patterns first described in Billy’s article and later in his S#harp architecture.



<< Previous NCharlie: TDD – part1, Next NCharlie: TDD – part 3 >>

No comments:

Post a Comment