4

This is in summary the TDD method:

  1. Write a test
  2. check if thes test fails
  3. write production code
  4. run test

I think that TDD as presented works only in ideal circumstances. I'll take a simple example:

Specification: Write a program that calculates the square root of a number, the user must enter a number. If the number is negative, the program should display an error, and if the number is positive or zero the program should display the number whose square is the number entered, or for decimal number whose square is closest to the number entered.

Writing test

class SquareRootTest {

   private SquareRoot squareRoot;

   public void testNegativeNumber() {    
      assertException(squareRoot.execute(-5);   
   }

   public void testIntegerSquareRoot() {
      assertEqual(squareRoot.execute(9),3);
   }

   public void testDecimalSquareRoot() {
     assertEqual(squareRoot.execute(3),1.732);
   }
}

run failed test

writing production code:

class SquareRoot {

 public double execute(Number number) {
     if(number < 0) 
         throw exception;
     if(number < 1)
         return squareRootLessThanOne(number);
      else
         return squareRootGreaterThanOne(numner);
   }

//private methods....

}

this is when writing production code, I find that I have to deal with the numbers lower than one differently from those numbers greater than 1. So I need to update my tests to reflect this.

Updating test

class SquareRootTest {

   public void testNegativeNumber() {    
       assertException(squareRoot.execute(-5));   
   }

   public void testIntegerSquareRoot() {
       assertEqual(squareRoot.execute(9),3);
   }

   public void testDecimalSquareRoot() {
      assertEqual(squareRoot.execute(3),1.732);
   }

   public void testNumberLowerThanOne() {
      assertEqual(squareRoot.execute(0.04),0.200);
   }
}

If I had found a single algorithm to calculate the square root, I would not change my tests.

The TDD method focuses solely on tests from specifications, yet there are tests that are derived from the implementation, particularly all conditional instructions and all instructions controlled by loops should normally be tested. These tests case can not be detected at the time of specification.

My question: How does TDD will deal with this situation? or is effectivelly a weakness of the TDD?

Belin
  • 145
  • 1
  • 1
  • 4
  • 2
    possible duplicate of [Are "TDD Tests" different to Unit Tests?](http://programmers.stackexchange.com/questions/86656/are-tdd-tests-different-to-unit-tests) See also: [What are the disadvantages of test-first programming?](http://programmers.stackexchange.com/questions/5560/what-are-the-disadvantages-of-test-first-programming) and [Is unit testing or test-driven development worthwhile?](http://programmers.stackexchange.com/questions/140156/is-unit-testing-or-test-driven-development-worthwhile) – gnat May 07 '13 at 11:53
  • 18
    What you have described is for many reasons not TDD. Two most obvious are that rather than writing **a** test, you have started with three, and that you have missed the "Do the simplest possible thing to make the test pass" step and replaced it with "write production code" which is not at all TDD. You have jumped directly to an implementation. By jumping to an implementation your tests will tend to be more tightly coupled to that implementation. – David Hall May 07 '13 at 12:06
  • You're lucky the specification included handling the negative number. Do you think every database app spec address NULL? – JeffO May 07 '13 at 12:29
  • "Do the simplest possible thing to make the test pass" in my example, when to add the case where number is lower than 1? when to add the test for that case? – Belin May 07 '13 at 12:57
  • 4
    @Belin: You are supposed to add **ONE** test (preferably with at most one assertion), write code to make that **ONE** test pass, and continue on with the next **ONE** test. – Spoike May 07 '13 at 13:12
  • @Belin: it is a common misbelieve that TDD is a "black-box" approach - writing tests without looking into the implementation, only into the spec. That is a fallacy - TDD is indeed a white-box method - you write tests for what your current implementation is missing. – Doc Brown May 07 '13 at 13:31
  • 1
    @Belin Spoike has said what I would have - you should only be writing one test at a time. These tests can come from various places - the spec, known bugs or limitations in the current code, or known parts of the implementation that need testing (for example if you introduce an if statement, you know you need to test both branches). As you add these new tests you will often break (and then fix) earlier tests - this is part of the methodology. Of course TDD is also a pragmatic methodology, so these *rules* are often breakable, but when learning the approach it is advised to follow them closely. – David Hall May 07 '13 at 13:36
  • In the TDD methodology, when you made ​​the analysis and design of your program? in the example given the design of the square root algorithm is done before writing tests? after? thank you – Belin May 08 '13 at 07:08
  • 1
    @Belin: requirement analysis is *not* a TDD activity, it is done separately and before you code (but it is up to you if you try to do *all* analysis beforehand or only for one requirement at a time). Design is something the TDD evangelists claim the test-first approach contains somehow - but IMHO TDD will primarily improve your program's testability, not less, not more. Most other design aspects are not in the focus of TDD - as a good designer you will (still) have to think about a problem, find a solution, and *then* start coding (TDD style, if you like). – Doc Brown May 13 '13 at 21:11
  • What is not clear to me yet is how a square root algorithm should emerge incrementally by adding tests one at a time: no matter how many special cases you test, in order to implement the final algorithm you have to analyse the problem and solve it. IMO TDD does not help much in this case. – Giorgio Apr 14 '14 at 20:32

2 Answers2

20

The TDD method focuses solely on tests from specifications

No. It doesn't. TDD encourages adding tests based on your implementation. At each stage of your implementation you are looking for a test that will fail. You add that test, and then make it pass.

So in your case, when you found that numbers less than one have to be deal with differently, you'd start by adding a test for those numbers which failed your implementation. Then you'd add the less than one version.

Winston Ewert
  • 24,732
  • 12
  • 72
  • 103
  • No, Test *Driven* Development means start with the test and then add implementation. This is not in keeping with the common frameworks for unit testing however, so people have tended to use the term BDD for the original meaning of TDD. Take a look at [Cucumber](http://cukes.info/) for the concept. What you describe is writing code, and then writing a test to prove what you've done works. BDD says write the test as a "spec" and then write code that fulfils that specification so you know what you've written does what its supposed to, and works too. – gbjbaanb May 07 '13 at 19:10
  • 2
    @gbjbaanb, I'm not saying you shouldn't write the test first. In fact, that's what my second paragraph says to do. But you should expect to come up with more tests that you need while writing your code. – Winston Ewert May 07 '13 at 21:15
14

One test at a time. You say it should throw an exception if given a negative argument?

public double execute(Number number) {
    throw exception;
}

Now it passes the first test.

You say it should return 3 if given 9?

public double execute(Number number) {
    if (number.equals(9))
        return 3;
    throw exception;
}

Now it passes both tests.

But you know you're not done. You know you've just been putting in dummy values, dummy logic. You add tests to drive it to a correct solution, and you'll know when it's easier to put in the correct solution than to add yet another dummy branch.

I believe the problem you are having in understanding TDD is the thought that all tests are written up front. They're not. Write one, make it pass. Write the next one, make it pass (without breaking the first one). Refactor when green. That is TDD, not translating a full spec into tests and then making them pass.

Carl Manaster
  • 4,173
  • 18
  • 31
  • At what point analysis or design are made ​​when using TDD? Using the example I gave. When do you analyze and design the algorithm? – Belin May 08 '13 at 06:54
  • The design of the square root algorithm is done before or after writing the test? – Belin May 08 '13 at 07:11
  • 2
    The process of design is not a momentary event, nor is it a stage. Design happens throughout the course of development, particularly while you are choosing what test to write next. It also happens while you refactor, safely, with tests to ensure your changes don't break existing behavior. – Carl Manaster May 08 '13 at 13:39
  • When designing the algorithm to calculate the square root of a number, we can express this algorithm using a pseudo language to draft or write directly in the programming language. In this case at least a test code skeleton is already written before writing of the test. So TDD is not really respected because in TDD is to write the test before the code! – Belin May 08 '13 at 16:18
  • Yes, you can do that for design. If you do that, what you are doing is not TDD. If you do TDD, then the process of design is as I have described above. There are many ways to design code; TDD is only one of them. It's well defined and we run into communication problems when we label something that is not TDD as TDD. – Carl Manaster May 08 '13 at 17:50
  • Abe Lincoln asked, "If you call a tail a leg, how many legs does a dog have?" The correct answer is four, because calling a leg doesn't make it a leg. And I fear this is the kind of conversation we are having here. – Carl Manaster May 08 '13 at 17:52
  • In summary, if I have understood you correctly, the TDD method is not applicable to all situations in programing – Belin May 11 '13 at 09:46
  • The TDD method does not apply when you are not using it. There are many approaches to designing software; it is just one. Like other approaches, it applies only when you choose to use it. – Carl Manaster May 11 '13 at 18:06
  • @Belin: just because you solve the algorithmical problem of root finding *beforehand* in pseudocode, I think you are still doing TDD as long as you code your tests and let them "drive" your final implementation in real code. TDD is "test driven development" - I strongly disagree to the folks who insist it means "test driven design". TDD makes your root finding implementation better unit-testable, that is IMHO the only design aspect which is improved by TDD. – Doc Brown May 15 '13 at 20:45
  • The use of TDD does not mean that you are not allowed to think about the design first, just that you are receptive to what the tests tell you - see [Uncle Bob's article](https://sites.google.com/site/unclebobconsultingllc/home/articles/the-scatology-of-agile-architecture) on this topic – DNA Jul 04 '13 at 21:23