34

I have always seen the recommendation that we should first write unit tests and then start writing code. But I feel that going the other way is much more comfortable (for me) - write code and then the unit tests, because I feel we have much more clarity after we have written the actual code. If I write the code and then the tests, I may have to change my code a little bit to make it testable, even if I concentrate much on creating a testable design. On the other hand, if I write the tests and then the code, the tests will change pretty frequently as and when the code shapes up.

As I see a lot of recommendations to start writing tests and then move on to coding, what are the disadvantages if I do it the other way - write code and then the unit tests?

1
  • 8
    +1 for asking why a certain practice is a "best practice" before embracing it Commented Jan 13, 2011 at 0:46

9 Answers 9

40

Red is the answer. Red is what you get from TDD's red-green-refactor cycle that you can't get, test-last. First, write a failing test. Watch it fail. That's your red, and it's important. It says: I have this requirement and I know my code isn't satisfying it. So when you go to step 2 (green), you know, with just as much certainty, that your code now is satisfying that requirement. You know that you've changed your code base in such a way as to satisfy the requirement.

Requirements (tests) developed after the code, based on the code, deprive you of that kind of certainty, that confidence.

11
  • 8
    Tests != Requirements. Both the tests and the code should be derived from the requirements. Commented Jan 13, 2011 at 10:17
  • 2
    @Bart van Ingen Schenau: The strength of TDD is precisely that tests ARE requirements. Moreover, they are executable requirements.
    – mouviciel
    Commented Jan 14, 2011 at 11:38
  • 1
    @Bart: Unit tests are often too detailed for (high level) customer requirements, but the idea definitely holds, especially if we also consider higher level tests such as automated acceptance tests which, once written, should be definitive requirements. That is the essence of "agile acceptance testning". Commented Jan 14, 2011 at 13:00
  • 1
    Yes, I am pretty doubtful about tests == requirements. I could however accept that a combination of tests == requirements.
    – k25
    Commented Jan 14, 2011 at 14:19
  • 3
    TDD is not about testing, it is about specification. Tests built in a TDD approach are a communication means between developer and customer for agreeing on what product should be made.
    – mouviciel
    Commented Jan 14, 2011 at 19:48
18

If you write the code, and then the tests, it's all too easy to fall into the trap of writing the tests so that the code passes, rather than writing the tests to ensure the code meets the specification.

That said, it's definitely not the only way to do things, and there is no "best" way of developing software. If you put a lot of upfront work into developing test cases, you don't know whether your proposed architecture has any flaws until much later - while if you developed the code first, you'll run into them sooner and can redesign with less sunk effort.

8
  • Yes you are right about the first point, but I always make sure I don't do that. If the test fails, I always go to the code and make sure its right and then see if my test is right, and then modify whichever is wrong. Thanks for your opinion, I will keep this in my mind.. +1 from me for the 1st point and the last point...
    – k25
    Commented Jan 13, 2011 at 1:01
  • 2
    But what if the test passes? The test might pass because it isn't actually exercising the code of interest; that can't really happen under TDD, because the test is supposed to fail initially, and does - if it doesn't, you don't go on to step 2 until you've fixed it. So there's a failure mode in test last that doesn't exist in test first. Commented Jan 13, 2011 at 1:21
  • @Carl Manaster - Yes, you do have a valid point. After I write the code, I am perfectly aware of the requirements and so my unit test case would be right (ideally). If my test case passes, I would say the code is right, if the test fails, I will follow what I said. But, I 100% agree that you have a valid point there.
    – k25
    Commented Jan 13, 2011 at 1:56
  • @k25: The point is that if your test case passes, you still don't know whether the code is right or not. The test case could be wrong.
    – Anon.
    Commented Jan 13, 2011 at 2:03
  • @Anon. - yes you are right, I will take this case also in to account.
    – k25
    Commented Jan 13, 2011 at 2:08
13

Actually, people get hung up on TDD is about testing, though they forget about the other two letters in the acronym. Something which can be read over here: TDD without the T or TDD is not about Testing.

The thing is I've learnt a plethora of other stuff that is tightly knit with TDD. It doesn't matter if you do test first: What matters is thinking about software design.

In order to even be able to write unit tests "the right way", i.e. so they are isolated, quick and automated, you'll hopefully notice that it takes some rethinking about how to arrange your code in a way that your code becomes easier to test.

Personally I learnt myself the SOLID principles without knowing there was such a thing written. This is because writing unit tests forced me to rewrite classes so they won't become overly complex to test. It led to things like:

  • I had to move functionality, that either didn't made sense or resided in private methods, over to separate classes so I could test them separately. (Single Responsibility Principle).
  • I had to avoid large inheritance structures and extend implementations with composition instead (prominent in Open-Closed principle).
  • I had to be smart about inheritance, I used abstract classes whenever I saw common code that could be shared and used stub methods (Liskov Substitution Principle).
  • I had to write interfaces and abstract classes so I could test classes in seperation. Which inadvertedly leads you to write mock objects. (Interface Segregation principle)
  • Because I wrote a lot of interfaces and abstract classes I started to declare variables and parameters to use the common type (Dependency inversion principle).

Even though I don't do test-first all the time, I do happen to follow good OO-principles and practices that you start to follow, just to make testing a bit easier. Now I am not writing code for its own sake. I wrote code so it can be easily tested or more importantly; easily maintained.

2
  • 1
    +1 for SOLID naturally occurs to you, when you think about software design.
    – ocodo
    Commented Jan 13, 2011 at 22:43
  • +1 (actually I wanted to give +10 but I can't). Exactly my thoughts - you list of points were extremely good. That's one reason I asked this question. I felt the classes became much more when I started writing the unit tests after I wrote the code. But I wanted to see the advantages/disadvantages of both the sides, thanks for your opinions!
    – k25
    Commented Jan 14, 2011 at 14:23
11

All of the other answers are good, but there is one point that wasn't touched on. If you write the test first, it ensures that tests get written. It is tempting, once you've written working code, to skip the tests and just verify it through the UI. If you have the discipline to always have a failing test before you write code, you can avoid this trap.

0
4

If you write your tests first, it gives you another chance to think about your design, before that design is "cast in stone."

For example, you may think that you need a method that takes a certain set of parameters. And if you wrote the code first, you'd write it that way and make the test fit the specified parameters. But if you write the test first, you might think "wait a minute, I wouldn't want to use this parameter in mainline code, so maybe I should change the API."

3
  • +1 for the first point. But, not getting to the parameters level, what if the design was discussed with others and accepted?
    – k25
    Commented Jan 13, 2011 at 2:27
  • @k25 - if something is hard to use as designed, it needs more thought. Sometimes -- very rarely -- it's just a hard task. But more often, it can be reduced to simpler tasks. I don't have a link, but either Gosling or Goetz did an interview about API design a few years ago ... worth Googling.
    – Anon
    Commented Jan 13, 2011 at 13:54
  • sure, thanks for the pointers, I will certainly look in to them...
    – k25
    Commented Jan 14, 2011 at 14:24
2

As I see a lot of recommendations to start writing tests and then move on to coding,

There's a real good reason for this.

If you say "do what feels right", people do the dumbest, craziest things.

If you say "write tests first", people at least might try to do the right thing.

what are the disadvantages if I do it the other way - write code and then the unit tests?

Usually, a lousy test and a design that has to be reworked to be testable.

However, that's only a "usually". Some people evolve the designs and tests in parallel. Some people put testable code in place and write tests with no rework.

The "Test First" rule is specifically there to teach and instruct people who have no clue at all.

In a similar way, we're told always to look "both ways" before crossing the street. However, we actually don't. And it doesn't matter. I live in a right-hand drive country and I only need to look left when starting to cross.

When I visit a left-hand drive country, looking left only could get me killed.

The rules are stated very strongly for a reason.

What you do is your own problem.

0
2

the point of writing the test first is it makes you think about

  • how to test the code
  • the interface the code must present to be testable

if you're doing something simple, it probably doesn't matter which one you write first (though it is good to cultivate the test-first habit) as the test will be simple and the interface will be obvious

but TDD scales up into acceptance tests, not just unit tests, and then the interface becomes non-trivial.

0
1

First off if you do not write your tests first then you are not doing Test Driven Development (TDD). The benefits are numerous and often hard to believe until you practice it multiple times. Here are the benefits that I have received doing TDD over traditional development:

  1. A safety net of tests - allows you to make big changes without the fear of breaking something unknowingly
  2. Organic design - the design I end up with is usually different that the design I would have done from scratch and it has always been better
  3. Productivity - working towards small goals (pass this one test) and making it (all tests pass) works really well for me and keeps me motivated. Add in a pair and my productivity reaches new highs.

Books: Beck, K. Test-Driven Development by Example

Good example: http://jamesshore.com/Blog/Lets-Play/

1
  • +1 - nice points (especially the 1st) and thanks for the links!
    – k25
    Commented Jan 14, 2011 at 14:36
0

When you write a test, how do you know it will detect a fail condition? The answer is "test the test". How you do that is to write the test first, see it fail, and only see it pass when the unit under test has been coded successfully (the red/green/refactor cycle mentioned in one of the other answers).

Writing the code first and then the test leaves open the question of whether the test would show an honest fail.

Remember that your tests express specification. If you have to revise your tests as your code "shapes up", it suggests that your specifications are changing. That may or may not be a good thing. It could mean that your understanding of the problem was not initially correct. On the other hand, it could mean that you're testing "how" the unit is doing its job rather than what it is supposed to be accomplishing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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