views:

172

answers:

7

I'm trying to get my head around TDD methodology and have run into - what I think is - a chicken-and-egg problem: what to do if a bug fix involves the changing of a method's signature.

Consider the following method signature:

string RemoveTokenFromString (string delimited, string token)

As the name suggests, this method removes all instances of a token from delimited and returns the resultant string.

I find later that this method has a bug (e.g. the wrong bits are being removed from the string). So I write a test case describing the scenario where the bug occurs and make sure that the test fails.

When fixing the bug, I find that the method needs more information to be able to do its job properly - and this bit of information can only be sent in as a parameter (the method under test is part of a static class).

What do I do then? If I fix the bug, this compels me to change the unit test - would that be 'correct' TDD methodology?

A: 

Red, Green, Refactor.

Whatever you do, you first want to get to a state where you have a compiling but failing test case that reproduces the bug. You then can proceed on adding just the parameter to the test and the implementation, but do nothing with it so you still have Red.

eed3si9n
A: 

If a method is not doing the job correctly then it needs to be fixed and if the fix requires change in signature then noting wrong in that. As per TDD you write the test case first which will certainly fail and then you write the method to satisfy the test. As per this approach if the method call in test requires a parameter for it to function then you need to do it.

Bhushan
+4  A: 

There is absolutely nothing wrong with bombing your tests, when you discover that the intended behaviour of the unit changes.

//Up front
[Test]
public void should_remove_correct_token_from_string()
{
  var text = "do.it.correctly..";
  var expected = "doitcorrectly";
  Assert.AreEqual(StaticClass.RemoveTokenFromString(text, "."), expected);
}

//After finding that it doesn't do the right thing
//Delete the old test and *design* a new function that
//Does what you want through a new test
//Remember TDD is about design, not testing!
[Test]
public void should_remove_correct_token_from_string()
{
  var text = "do.it.correctly..";
  var expected = "doitcorrectly";
  Assert.AreEqual(
      StaticClass.RemoveTokenFromString(
          text,
          ".",
          System.Text.Encoding.UTF8), expected);
}

//This will force you to add a new parameter to your function
//Obviously now, there are edge cases to deal with your new parameter etc.
//So more test are required to further design your new function
Jim Burger
+1  A: 

There is a refactoring called Add Parameter that could help here.

If your language supports method overloading, you could create the new function with the new parameter first, copying the body of the existing function and fixing your problem.

Then when the problem is fixed, you can modify all the tests, one by one, to call the new method. Last you can delete the old method.

With a language that does not support method overloading, create a new function with a different name, copy the body on the existing function in that new function, have the existing function calling the new function, possibly with a dummy value for the new parameter. Then you could have all your tests passing. Make your old tests call the new function, one by one. When the old method is not used anymore, it can be deleted and the new function renamed.

This is a bit process-extensive, but I think this is the TDD way to follow red-green-refactor.

Default value for parameter could also help, if there are available.

philippe
+2  A: 

Keep it simple.

If your unit test is wrong, or obsolete, you have to rewrite it. If your specs change, or certain specs are no longer relevant, your unit tests have to reflect that.

Red, green, refactor also applies to your unit tests, not just the code you are testing.

Jon Limjap
Amen to that, well said.
Jim Burger
A: 

I'd say don't fret about the 'right'/'correct' way... whatever helps you get you closer to the solution quicker.

If you find that you need to take in an extra parameter,

  • update the call in the test case
  • add the new parameter to the actual method
  • verify that your code builds and the test fails again.
  • proceed with making it green.

Only in cases where adding a new parameter would result in zillions of compile errors, would I recommend - taking it in baby steps... you dont want to update the whole source base before finding out you really didn't need the third param or you need a fourth one.. time lost. So get the new version of the method 'working' before updating all references. (As philippe says here)

  • write a new overload with the added parameter
  • Move the code of the old method into the new overload
  • Make the old overload relay or delegate to the new overload with some default value for the new param
  • Now you can get back to the task at hand and get the new test to go green.
  • If you dont need the old overload anymore, delete it and fix the resulting compile errors.
Gishu
+1  A: 

You have fallen into the most dangerous trap in TDD: you think TDD is about testing, but it isn't. However, it is easy to fall into that trap, since all the terminology in TDD is about testing. This is why BDD was invented: it is essentially TDD, but without the confusing terminology.

In TDD, tests aren't really tests, they are examples. And assertions aren't really assertions, they are expectations. And you aren't dealing with units, you are dealing with behaviors. BDD just calls them that. (Note: BDD has evolved since it was first invented, and it now incorporates things that aren't part of TDD, but the original intention was just "many people do TDD wrong, so use different words to help them do it right".)

Anyway, if you think of a test not as a test, but a behavioral example of how the method should work, it should become obvious that as you develop a better understanding of the expected behavior, deleting or changing the test is not only allowed by TDD, it is the only correct choice! Always keep that in mind!

Jörg W Mittag