I am trying to practice TDD.
My understanding is that TDD should go like this
- Write a test list for the interface/class I am going to develop.
- Start with the easiest not implemented test from my test list.
- Write the test, no implementation code yet.
- Write the interface of the class to make the code compile.
- Run the test resulting in one failing test.
- Write the implementation making the test pass.
- Refactor the mess I’ve made.
- Goto 2.
The problem I have is when writing the implementation or doing the refactoring. I often come to the conclusion that the implementation I just wrote should be delegated to another class.
What should a true TDD’r do at this point?
- Leave the existing test list alone for a while and create a new one for the newly discovered class (the same problem can manifest itself when implementing the new class offcourse)
- Go the Interaction Based way of testing and Mock the new class, continue with the testcases of the class you are working on and come back later to create a correct implementation of the mocked class.
- This situation should not present itself. I probably have not thought out my initial design well enough. (but wouldn’t that defeat one of the purposes of TDD?!).
I’d love to know how other people handle these situations.
Don’t look for a one-to-one relationship between your tests and your classes. If you decide to introduce a new class, let that be a refactoring supported by the original test, and add tests in the appropriate place (where that is depends on the specific case) when you want to add functionality (or to test eventualities you need to cover that you didn’t test for yet).
I would add that the main success in TDD is to get into the rhythm of red-green-refactor. When you feel the benefit of that rhythm, you have started to “get” it. That isn’t to say you will find it worthwhile in all cases, but until you feel that rhythm you haven’t gotten to what its advocates like about it.
And there is usually (especially in architecturally complicated applications, like n-tier applications) some amount of up-front design. Nothing sketched in stone, but enough to give the units a place to go. Of course the architecture may evolve in an agile methodology, but a general idea of the landscape needs to be there if there are multiple layers to the architecture.
EDIT: (In response to the comment). Should the new class get tested in its own right? Not necessarily. It depends if the class develops an importance of its own. When you are unit testing, you are testing a piece of functionality. It isn’t an integration test just because there are two classes involved. It becomes an integration test when two units start interacting. The boundary I typically think of is if I have to set up significant state in group-of-classes A to interact with group-of-classes B, and especially if group-of-classes A calls group-of-classes B and what I am interested in testing is how B reacted to A, then I’m looking at an integration test.