Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Today we were training TDD and found the following point of misunderstanding.

The task is for the input "1,2" return sum of numbers which is 3. What I have written (in C#) was:

numbers = input.Split(',');
return int.Parse(numbers[0]) + int.Parse(numbers[1]); //task said we have two numbers and input is correct

But other guys preferred to do it other way. First, for input "1,2" they made added the following code:

if (input == "1,2")
   return 3;

Then they introduced one more test for input "4,5" and changed implementation:

if (input == "1,2")
   return 3;
else if (input == "4,5")
   return 9;

And after that they said "Okay, now we see the pattern" and implemented what I initially did.

I think the second approach better fits the TDD definition but... should we be so strict about it? For me it is okay to skip trivial baby steps and combine them into "twinsteps" if I am sure enough that I won't skip anything. Am I wrong?

Update. I have made a mistake by not clarifing it was not the first test. There already were some tests so "return 3" actually wasn't the simplest piece of code to satisfy the requirement.

share|improve this question
22  
So tiny that my coworkers gush "Ooahhh dazso cuuuuuute" –  Adel Sep 22 '11 at 7:47
5  
@Adel: Almost choked on my breakfast, keyboard now full or spittle and crumbs –  Binary Worrier Sep 22 '11 at 8:06
2  
@Adel, as for non-native speaker, it is rather difficult for me to understand this humour but I guess your coworkers like the question :) –  Idsa Sep 22 '11 at 8:47
8  
@Idsa: It's transposing a coworkers response when shown a childs first steps "Ooahhh dazso cuuuuuute" = "Oh that is so cute" (spoken in a sing-song-isn't-that-very-cute voice), with their response when seeing Unit Tests written by Adel, looking at the Unit Tests baby steps they say "Oh that's so cute". Reaction to a - real - babies steps = reaction to unit tests "baby steps". –  Binary Worrier Sep 22 '11 at 9:00
3  
@Binaryworrier wish I could give you real points for taking the time to explain the parentess –  Andrew Finnell Sep 22 '11 at 11:55

9 Answers 9

up vote 26 down vote accepted

Write the simplest code that makes the tests pass.

Neither of you did that, as far as I can see.

Baby Step 1.

Test: For the input "1,2" return sum of numbers which is 3

Make the test fail:

throw NotImplementedException();

Make the test pass:

return 3;

Baby Step 2.

Test: For the input "1,2" return sum of numbers, which is 3

Test: For the input "4,5" return sum of numbers, which is 9

Second test fails, so make it pass:

numbers = input.Split(',');
return int.Parse(numbers[0]) + int.Parse(numbers[1]);

(Way simpler than a list of if...return)

You can certainly argue Obvious Implementation in this case, but if you were talking about doing it strictly in baby steps then these are the correct steps, IMO.

The argument is that if you don't write the second test then some bright spark could come along later and "refactor" your code to read:

return input.Length; # Still satisfies the first test

And, without taking both steps, you have never made the second test go red (meaning that the test itself is suspect).

share|improve this answer
    
Regarding your input.Length example, with same success I can imagine some mad incorrect implementation which won't be caught by both tests –  Idsa Sep 22 '11 at 7:48
    
@Idsa - Yes, absolutely, and the more tests you write, the more mad the implementation has to be. input.Length isn't that far fetched, especially if the input to the method happens to be a measurement from some file somewhere and you inadvisedly called your method Size(). –  pdr Sep 22 '11 at 7:54
5  
+1. With respect to how to learn TDD, this is the right way. Once you have learned it, you may sometimes go directly to the obvious implementation, but to get a feel for TDD, this is much better. –  Carl Manaster Sep 22 '11 at 14:28
1  
I have a question concerning the "test" itself. Would you write a new test for input "4,5", or modify the original test? –  mxmissile Sep 22 '11 at 19:51
1  
@mxmissile: I would write a new test. It doesn't take a whole lot of time and you end up with twice as many tests to protect you when you do refactoring later. –  pdr Sep 22 '11 at 20:21

I think the second way is mind numbingly stupid. I see the value in making small enough steps, but writing those tiny zygote (can't even call them baby) steps is just asinine and a waste of time. Especially if the original problem you're solving is already very small by it's own.

I know it's training and it's more about showing the principle, but I think such examples do TDD more bad than good. If you want to show the value of baby steps, at least use a problem where there is some value in it.

share|improve this answer
    
+1 and thanks for making me look up and learn a new word (asinine) –  Marjan Venema Sep 22 '11 at 7:59
12  
+1 for calling it numbingly stupid. TDD is all nice and such, but like with any modern hyped programming technique you should take care not to get lost into it. –  stijn Sep 22 '11 at 7:59
2  
"Especially if the original problem you're solving is already very small by it's own." - If the input were two ints to be added together, I would agree with this, but I'm not convinced when it's "split a string, parse two ints from the result and add them." Most methods in the real world aren't much more complicated than that. In fact, there should be more tests to come, to cover edge cases like finding two commas, non-integer values, etc. –  pdr Sep 22 '11 at 8:13
4  
@pdr: I agree with you that there should be more tests to handle the edge cases. When you write them and notice that your implementation needs to change to handle them, by all means do that. I guess I just have an issue with taking zygote steps to the first happy-path, "obvious implementation", instead of just writing that down and going from there. I don't see the value in writing out an if statement that every fiber in my body knows is just going to disappear the next moment. –  Christophe Vanfleteren Sep 22 '11 at 9:41
5  
+1 for "zygote steps". –  Mark Bannister Sep 22 '11 at 10:29

Kent Beck covers this in his book, Test Driven Development: By Example.

Your example indicates an 'obvious implementation' - you want to return the sum two input values, and this is a fairly basic algorithm to achieve. Your counter-example fall into 'fake it until you make it' (although a very simple case).

Obvious implementation can be much more complicated than this - but basically it kicks in when the specification for a method is pretty tight - for example, return a URL encoded version of a class property - you don't need to waste time with a bunch of faked encodings.

A database connection routine, on the other hand, would need a bit more thought and testing so there is no obvious implementation (even if you might have written one several times already on other projects).

From the book:

When i use TDD in practice, I commonly shift between these two modes of implementation, When everything is going smoothly and I know what to type, I put in Obvious Implementation after Obvious Implementation (running the tests each time to ensure that what's obvious to me is still obvious to the computer). As soon as I get an unexpected red bar, I back up, shift to faking implementations, and refactor to the right code. When my confidence returns, I go back to Obvious Implementations.

share|improve this answer

I see this as following the letter of the law, but not its spirit.

Your baby steps should be:

As simple as possible, but no simpler.

Also, the verb in the method is sum

if (input == "1,2")
   return 3;

isn't a sum, it's a test for specific inputs.

share|improve this answer

To me it seems fine to combine several trivial implementation steps into one slightly less trivial - I do that all the time too. I don't think one needs to get religious about following TDD everytime to the letter.

OTOH this applies only for really trivial steps like the above example. For anything more complex, which I can't fully hold in my mind at once and/or where I am not 110% sure about the outcome, I prefer to go one step at a time.

share|improve this answer

When first setting out down the road of TDD the size of steps can be a confusing issue, as this question illustrates. A questions I often asked myself when I first started writing test driven applications was; Is the test I am writing helping to drive my applications development? This may seem a trivial and unrelated to some but hang in there with me for a moment.

Now when I set out to write any application I will usually start with a test. How much of a step that test is largely relates to my understanding of what it is I am trying to do. If I think I have pretty much got the behaviour of a class in my head then the step will be a big one. If the problem I am trying to solve is a lot less clear then the step might simply be that I know there is going to a method named X and that it will return Y. At this point the method won't even have any parameters and there is a chance that the name of the method and the return type will change. In both cases the tests are driving my development. They are telling me things about my application:

Is this class that I have in my head actually going to work?

or

How the hell am I even going to do this thing?

The point is that I can switch between big steps and little steps in the blink of an eye. For example if a big step doesn't work and I can't see an obvious way around it I will switch to a smaller step. If that doesn't work I will switch to an even smaller step. Then there are other techniques such as triangulation if I get really stuck.

If like me you are a developer and not a tester then the point of using TDD is not to write tests but to write code. Don't get hung up on writing loads of small tests if they aren’t given you any benefits.

I hope you enjoyed your training an stick with TDD. IMHO if more people were test infected then the world would be a better place :)

share|improve this answer

In a primer about unit testing i read the same approach (steps that look really, really tiny), and as an answer to the question "how tiny should they be" something i liked, which was (paraphrased) like this:

It's about how confident you are that the steps work. You can make real big steps if you want. But, just try it for some time and you will find a lot misguided confidence in places you take it for granted. So, the tests help you to build a fact-based confidence.

So, maybe your collegue is just a little shy :)

share|improve this answer

Isn't the whole point that the implementation of the method is irrelevant, as long as the tests succeed? Extending the tests will fail quicker in the second example, but can be made to fail in both cases.

share|improve this answer
1  
It is irrelevant if you totally don't care about wasting your time –  Idsa Sep 22 '11 at 13:28

I'm agreeing with the people saying that neither is the simplest implementation.

The reason the methodology is so strict is that it obliges you to write as many relevant tests as possible. Returning a constant value for one test case and calling it a pass is fine because it forces you to go back and specify what you really want in order to get anything other than nonsense from your program. Using such a trivial case is shooting yourself in the foot in some respects, but the principle is that mistakes creep into the gaps in your specification when you try to do 'too much' and rendering down the requirement to the simplest possible implementation ensures that a test must be written for each unique aspect of behaviour that you actually want.

share|improve this answer
    
I have added an update regarding "returning a constant value" –  Idsa Sep 22 '11 at 16:41

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.