Saturday, May 23, 2009

Revving up the TDD engine

tach

I’m still in the early stages of writing unit tests at work… but I did commit 26 passing tests to source control this afternoon!  The pain is fading, I’m gaining confidence and speed and I’m getting excited about the number of tests I’ve written.  We’re no mature TDD shop yet, but it is an enjoyable stage when the shiny new unit tests still make us go “ah”.

In this post I’ll finish out the TDD section of my NCharlie project series by walking through some code that tests the interaction between a controller and view.  It includes using Rhino Mocks to raise an event and verify that the controller calls the correct method on the view in response.  I know in my last post I practically gave up on Rhino Mocks in favor of writing my own mock classes and methods… well, I changed my mind.  RM is a great tool, and I’m getting used to the syntax required for .Net 2.0 projects.  So here goes…

search-results

I want to test the behavior when a user clicks one of the “[show]” links in the Task Search results list.  The application should show the Task Detail view when this happens:

task-detail

So I’ll name my test accordingly:

[Test]
public void Should_call_show_task_when_task_selected_event_is_raised()
{
}


Before I start into the body of this test method, there is some setup code that I need to get in place:



[TestFixture]
public class TaskManagerControllerTests
{
MockRepository mocks;
ITaskManagerView viewMock;
ITaskRepository repository;

[SetUp]
public void SetUp()
{
mocks = new MockRepository();
viewMock = mocks.DynamicMock<ITaskManagerView>();
repository = mocks.CreateMock<ITaskRepository>();
}



The mock view will allow me to test my controller code without needing to have a view implementation (ASPX page) wired up and working first.  First I arrange the needed elements for the test by creating the event arguments object that will be passed with the event and an event raiser named “clickEvent”:



[Test]
public void Should_call_show_task_when_task_selected_event_is_raised()
{
string taskID = "12345678-1234-1234-1234-123456789012";
TaskSelectedEventArgs e = new TaskSelectedEventArgs(taskID);

IEventRaiser clickEvent = Expect
.Call(delegate { viewMock.TaskSelected += null; })
.IgnoreArguments()
.GetEventRaiser();



Next I use the Rhino mocks “Expect.Call” method to state that I expect the view’s “ShowTask” method to be called with with my test task ID as the parameter.  The expectations are finished with a call to the R.M. “ReplayAll” method:



[Test]
public void Should_call_show_task_when_task_selected_event_is_raised()
{
string taskID = "12345678-1234-1234-1234-123456789012";
TaskSelectedEventArgs e = new TaskSelectedEventArgs(taskID);

IEventRaiser clickEvent = Expect
.Call(delegate { viewMock.TaskSelected += null; })
.IgnoreArguments()
.GetEventRaiser();

Expect.Call(delegate { viewMock.ShowTask(e.TaskID); })
.Repeat.Once();
mocks.ReplayAll();
}



Finally, I execute the code that will test this behavior… I instantiate a new TaskManagerController (this happens when the view is created) and raise the click event.  The R.M. “VerifyAll” method verifies that all my expectations are met when the code executed:



[Test]
public void Should_call_show_task_when_task_selected_event_is_raised()
{
string taskID = "12345678-1234-1234-1234-123456789012";
TaskSelectedEventArgs e = new TaskSelectedEventArgs(taskID);

IEventRaiser clickEvent = Expect
.Call(delegate { viewMock.TaskSelected += null; })
.IgnoreArguments()
.GetEventRaiser();

Expect.Call(delegate { viewMock.ShowTask(e.TaskID); })
.Repeat.Once();
mocks.ReplayAll();

new TaskManagerController(viewMock, repository);
clickEvent.Raise(null, e);
mocks.VerifyAll();
}


That’s it.  The code to make it pass is painfully simple (painful because the code to test it is so much longer… I’m getting less and less offended by this state of affairs though, since the tests look like this for complicated code under test as well.  Besides, these tests copy and paste very easily!)  Here is the code from the controller under test:



private void OnTaskSelected(object sender, TaskSelectedEventArgs e)
{
view.ShowTask(e.TaskID);
}


And that makes it pass:



tests-passed



 



 

Thursday, May 14, 2009

Do we have time to write tests on our projects?

This is a question I would like to answer in the context of my day job.  It came up again today as I was talking with the other developers about some of the “new methods” I would like to integrate into our development process.  The title of this post is how the question sounds now in my head.  This afternoon it went more like this: “We don’t have time to write tests on our projects!”

Granted the developer who made this statement is a big believer in the “speak first, reason it out later” method of communicating… this is his knee-jerk reaction to someone telling him we would write more code for the same functionality.  I can sympathize with him… we have deadlines on our projects to meet.  But I don’t agree with him.  I’ve already written some tests at work to solve a problem in the business logic of software we released 2 months ago.  The exercise helped me find other problems in that code and refactor it to a state that will be much easier to maintain.  Do we have time to maintain legacy code that is hard to understand and has no tests written against it?  I don’t.

But… even though I am convinced we need to write tests, I want to be practical.  Will my team benefit from this?  Are there certain types of developers or certain types of projects that will never see the benefit of writing tests against code?  Maybe so – but I hope that isn’t a description of the team I work with.  And I think the best way to find out is to do the best I can to set us up for success.  That is a big reason I am writing about my experience building up the NCharlie project.  I have some work to do before we are there though.  As I mentioned before, TDD still feels like a pain point for me because of the way my tests look using Rhino Mocks to mock objects like the database access code.  When I mentioned this pain point to my boot-camp instructor his advice was to “abandon ship”… not on TDD, but on the Rhino Mocks Expect-Replay-Verify model and use the easier to understand Arrange-Act-Assert model of testing (I know AAA is possible with Rhino Mocks… but not with nice syntax in .Net 2.0).

So, I am going to follow my instructor’s advice and abandon ship.  I’m going to start writing tests without a mocking framework.  I can create a class quickly that mocks a database access class without using Rhino Mocks.  Sure it will be a bit more code to write, but I think the clarity it will bring to the tests will be worth it.  I also think it will be a good exercise even if I do end up coming back to a mocking framework like Rhino Mocks… I will appreciate it more after manually building mock objects.

This means I am going to stay on the topic of writing tests for at least one more post.  Earlier in this series I wrote some tests to verify that the controller class for the Task Search page in the NCharlie project was correctly responding to events raised in the view.  I didn’t show the code for the test in the post (it was available in the code repository) because I did not want to distract from the intent of that post.  Now it is time to show that code… almost (actually, in the next post).  First, let me answer the question I posed in the title of this post:

Yes, we do have time.  It will make us go faster because our code will end up being easier to work in now, and easier to extend or fix later.

Tuesday, May 5, 2009

TDD Pain Points, A Retrospective

This is the fourth weekend into writing about test driven development… taking agile methods I learned in boot-camp back to work.  Time for a retrospective…

First I will say that writing about this process has been a tremendous help in making these concepts gel.  Try teaching someone about something you are trying to learn.  I don’t care if no one ever reads these posts – the process of writing them has been invaluable to me.

Now, for the pain points:

Supervising controller:

I chose this pattern (a flavor of Model-View-Presenter) for two reasons.  1) We use .Net 2.0 at work and our developers have a lot invested in webforms.  2) It helps me separate functionality away from code-behind files and adhere to the Single Responsibility Principle, which is especially important if the code will be tested.

I like most of the Supervising Controller pattern in the context of webforms.  What I don’t like (pain point) is the "loop" of event handling: view fires event, then raises another event that the controller is subscribed to, then the controller responds by executing some logic and then calling a function back in the view to render results.  This is convoluted... I like the separation of concerns, but I don't like having to subscribe to events more than once.  Can I just call controller methods directly from the view?  Maybe what I'm describing is called the "Passive Controller"... I need to look into it.  This is really, probably, a pain point that should be attributed to webform development.  But hey, webforms pay the bills!

TDD:

Yes, as of now I’m listing Test Driven Development as a pain point.  I really like testing my code.  I don't really like the syntax I'm using to mock functionality.  Mocking my dependencies is necessary to write unit tests, but the Record, Replay, Verify syntax in Rhino Mocks is not natural for me to set up and harder to explain to my team.  I am still trying to shove the Rhino Mocks 3.3 RRV syntax into the Arrange, Act, Assert (AAA) method I learned in boot-camp.  I need to change the way I'm using Rhino Mocks, or I need to start using a different mocking framework (Rhino Mocks supports the AAA syntax and is explained really well... Using .net 3.x lamba expressions.  AAA is possible with Rhino Mocks, but the 2.0 syntax is painful.

That’s it for pain points for now.  These by no means mean I am giving up on TDD or Supervising Controller.  Although painful right now, I think these are birth-pangs… I am hopeful that in 2 or 3 months they will be big giant assets in my toolbox.  I’ll end now on a (what is the opposite of a pain point?) happy point: Resharper 4.5 is incredible.  It makes me go faster… I intend to dedicate a future post to my top X favorite features.

<< NCharlie: TDD – Part 3 (a UI test)

Geo Tracks – 2009 May Cinco

I’m a little late this week because we moved last weekend.  Now I am closer to work (cut 2 hours per day off the commute!)… which means less time with the podcasts and more time with the family – a nice trade I think.  I’m still listening to book one in the Wheel of Time series, and will be for while.