Some confusion about test driven development

125 views Asked by At

According to wikipedia following are the steps in TDD:

step 1: Write a unit test

step 2: run the unit test

step 3: write code for module

step 4: run ALL tests again

step 5: clean up code

step 6: repeat the steps

Question 1 : in TDD we mainly write unit tests. Where do integration tests and system tests fit in the above steps?

Consider the following example:

Suppose we have a feature which we have to develop. Then according TDD, we will develop this feature in short iterations. Suppose we have divided this feature into 2 modules – module 1 and module 2. I am writing iteration steps for developing these modules in TDD according to above given steps. (Question 2) Please correct me where I am wrong in the following steps:

**Iteration 1: **

Step 1: we write unit test for module 1.

Step 2: Run this unit test for module 1(this test would fail)

Step 3: Develop code for module 1.

Step 4: Run unit tests for module 1 again(this test would pass)

**Iteration 2: **

Step 1: write unit test for module 2

Step 2: run unit test for module 2

Step 3: write code for module 2

Step 4: run unit tests for module 1 and unit tests of module 2.(Question 3: Here in this step why are we running unit tests of module 1 as module 2 is in under test? If you say that is done because to test that whether module 2 does not break module 1 functionality then my question is that here we are testing only module 2. It is not integrated with module 1 yet so how it will break module 1?)

**Iteration 3: **

Step 1: create integration tests (Am I right here?)

Step 2: run integration tests (they will fail as module 1 and module 2 are not integrated yet)

Step 3: Integrate module 1 and module 2

Step 4: run all tests (unit tests of module 1, module 2 and integration tests)

(Question 4: Why in this step are we running unit tests of module 1 and 2?)

Iteration 4(when all modules are integrated):

Step 1: create system tests

Step 2: run system tests (they will fail)

Step 3: (Question 5:) What code should I write here as for system testing we do not write any code and according to TDD principles first we write tests then we write development code so what code we will write here?

2

There are 2 answers

2
Sam Holder On BEST ANSWER

In my opinion you have your workflow slightly backwards. In the excellent book Growing Object Oriented Software Guided By Tests it is recommended that you start out with a higher level test, often an 'integration test' that represents a feature you want to add and have this be the driver of the behaviour you want to see.

You know you have finished the feature when this integration test passes.

Once you have this failing test (which the authors refer to as the outer loop) then you start on the TDD process on the inner loop, ie creating tests for the classes you need to implement the required functionality. you do this by create a test, write the code to make it pass. Then run all tests. Likely your outer test will still fail. Then implement another unit test and then the required implementation. Repeat this process until all you have created all of the necessary classes for the outer test to pass.

Then repeat this whole process again by writing a new outer test.

For me the most important thing to do is to find a process that works for you, and to be pragmatic. Do not be forced down any dogmatic approach as, as with anything, writing software varies with the thing you are writing, the people you are writing it with, the tools available to you and many other factors. Always be prepared to change your own process and constantly reevaluate it, based on your own experiences but also on the experiences of those that you work with and that you respect. No one has the perfect solution as things can always get better.

The guy who wrote Rhino Mocks has said he rarely uses mocking frameworks anymore. The guy who wrote Dependency Injection in .NET says he rarely uses IoC containers anymore. Be flexible and pragmatic.

I find that I tend to focus more on the outer integration tests more than I focus on the unit level tests, as this forces the tests to test the behaviour of the code and not the implementation. I found that when I had one test class for every actual class that it became very expensive to refactor my code because not only did I have to change all the classes, but I had to refactor all the tests at the same time. When the tests are focussed on the behaviour of logical units of code then usually when I refactor the class structure of that code the tests stay the same as I am only reorganising the internal structure, not the external behaviour.

What @CarlManaster said about BDD is also very relevant. I find BDD more useful than TDD because mainly of the shift in emphasis from tests to behaviour. The behaviour is what you want to have, not a large suite of tests (although that can be good as well.)

As for which tests to run, for me the more tests you can run more often, the better. it's amazing how often a change which is 'isolated' can inexplicably cause something else to break. Holding the consequences of every change throughout the entire system in your head is not a simple as you might think even for small systems.

My preferred tool is NCrunch. It removes the stop/build/run tests part of coding . you just write tests, write code. wait a few seconds. go green. repeat. Changed my life; worth double what he charges.

6
Carl Manaster On

Test-Driven Development tends to focus on units and therefore on unit tests. An affiliated approach, Behavior-Driven Development, or BDD, drives design at a higher level and consequently tests at a higher level; acceptance tests and integration tests may play a role in BDD. TDD does not preclude integration tests, but they are not part of the practice. The practice is, as you say, to write a tiny test of a tiny bit of functionality at a time, make that test pass, clean up your code, and iterate. That's all unit tests.

If your first feature is working, then integrating your second working feature with it should work fine - at least in theory. TDD gives you those two working features, but of course you will need to integrate them, and of course you will need the tests to verify that the integration works. But that's not part of the practice of TDD. What TDD says is that when your integration test fails, that is the result of some smaller unit of functionality failing, and you have failed to adequately test that unit of functionality. Instead of trying to fix it, write the unit test which demonstrates the failure. Make that test pass, and your integration test will now pass as well. The same goes for system tests.