C# TDD – How to Get Started with Test-Driven Development

ctdd

I basically have the gist of TDD. I'm sold that it's useful and I've got a reasonable command of the MSTEST framework. However, to date I have not been able to graduate to using it as a primary development method. Mostly, I use it as a surrogate for writing console apps as test drivers (my traditional approach).

The most useful thing about it for me is the way it absorbs the role of regression testing.

I have not yet built anything yet that specifically isolates various testable behaviors, which is another big part of the picture I know.

So this question is to ask for pointers on what the first test(s) I might write for the following development task: I want to produce code that encapsulates task execution in the fashion of producer/consumer.

I stopped and decided to write this question after I wrote this code (wondering if I could actually use TDD for real this time)

Code:

interface ITask
{
    Guid TaskId { get; }
    bool IsComplete { get; }
    bool IsFailed { get; }
    bool IsRunning { get; }
}

interface ITaskContainer
{
    Guid AddTask(ICommand action);
}

interface ICommand
{
    string CommandName { get; }
    Dictionary<string, object> Parameters { get; }
    void Execute();
}

Best Answer

Starting with this concept:
1) Start with the behavior that you desire. Write a test for it. See test fail.
2) Write enough code to get the test to pass. See all tests pass.
3) Look for redundant / sloppy code -> refactor. See tests still pass. Goto 1

So on #1, let's say that you want to create a new command (I'm stretching to how the command would work, so bear with me). (Also, I'll be a bit pragmatic rather than extreme TDD)

The new command is called MakeMyLunch, so you first create a test to instantiate it and get the command name:

@Test
public void instantiateMakeMyLunch() {
   ICommand command = new MakeMyLunchCommand();
   assertEquals("makeMyLunch",command.getCommandName());
}

This fails, forcing you to create the new command class and have it return its name (purist would say this is two rounds of TDD, not 1). So you create the class and have it implement the ICommand interface, including returning the command name. Running all tests now shows all pass, so you proceed to look for refactoring opportunities. Probably none.

So next you want it to implement execute. So you have to ask: how do I know that "MakeMyLunch" successfully "made my lunch". What changes in the system because of this operation? Can I test for this?

Suppose it is easy to test for:

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   ICommand command = new MakeMyLunchCommand();
   command.execute();
   assertTrue( Lunch.isReady() );
}

Other times, this is more difficult, and what you really want to do is test the responsibilities of the subject-under-test (MakeMyLunchCommand). Perhaps the responsibility of MakeMyLunchCommand is to interact with Fridge and Microwave. So to test it you can use a mock Fridge and mock Microwave. [two sample mock frameworks are Mockito and nMock or look here.]

In which case you would do something like the following pseudo code:

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   Fridge mockFridge = mock(Fridge);
   Microwave mockMicrowave = mock(Microwave);
   ICommand command = new MakeMyLunchCommand( mockFridge, mockMicrowave );
   command.execute();
   mockFramework.assertCalled( mockFridge.removeFood );
   mockFramework.assertCalled( microwave.turnon );
}

The purist says test the responsibility of your class - its interactions with other classes (did the command open the fridge and turn on the microwave?).

The pragmatist says test for a group of classes and test for the outcome (is your lunch ready?).

Find the right balance that works for your system.

(Note: consider that perhaps you arrived at your interface structure too early. Perhaps you can let this evolve as you write your unit tests and implementations, and in step #3 you "notice" the common interface opportunity).

Related Topic