This might be a rather silly question as I am at my first attempts at TDD. I loved the sense of confidence it brings and generally better structure of my code but when I started to apply it on something bigger than one-class toy examples, I ran into difficulties.

Suppose, you are writing a library of sorts. You know what it has to do, you know a general way of how it is supposed to be implemented (architecture wise), but you keep "discovering" that you need to make changes to your public API as you code. Perhaps you need to transform this private method into strategy pattern (and now need to pass a mocked strategy in your tests), perhaps you misplaced a responsibility here and there and split an existing class.

When you are improving upon existing code, TDD seems a really good fit, but when you are writing everything from scratch, the API you write tests for is a bit "blurry" unless you do a big design up front. What do you do when you already have 30 tests on the method that had its signature (and for that part, behavior) changed? That is a lot of tests to change once they add up.

  • 3
    30 tests on one method? Sounds like that method has way too much complexity, or you are writing too many tests. – Minthos Dec 7 '12 at 20:09
  • Well I may have exaggerated a bit to express my point. After checking the code I generally have less than 10 methods per test with most of them being below 5. But the whole "going back and hand-changing them" part has been quite frustrating. – Vytautas Mackonis Dec 7 '12 at 20:29
  • 6
    @Minthos: I can think of 6 tests off the top of my head that any method taking a string will often fail or perform poorly against on a first-draft write (null, empty, too long, not localized properly, poor perf scaling). Similarly for methods taking a collection. For a non-trivial method, 30 sounds large, but not too unrealistic. – Steven Evers Dec 7 '12 at 21:01

What you call "big design up front" I call "sensible planning of your class architecture."

You can't grow an architecture from unit tests. Even Uncle Bob says that.

If you're not thinking through the architecture, if what you're doing instead is ignoring architecture and throwing tests together and getting them to pass, you're destroying the thing that will allow the building to stay up because it's the concentration on the structure of the system and solid design decisions that helped the system maintain its structural integrity.

http://s3.amazonaws.com/hanselminutes/hanselminutes_0171.pdf, Page 4

I think it would be more sensible to approach TDD from a perspective of validating your structural design. How do you know the design is incorrect if you don't test it? And how do you verify that your changes are correct without also changing the original tests?

Software is "soft" precisely because it is subject to change. If you are uncomfortable about the amount of change, continue to gain experience in architectural design, and the number of changes you will need to make to your application architectures will decrease over time.

| improve this answer | |
  • The thing is, even with "sensible planning" you expect alot of it to change. I usually leave about 80% of my initial architecture intact with some changes in between. Those 20% are what's bothering me. – Vytautas Mackonis Dec 7 '12 at 20:43
  • 2
    I think that's just the nature of software development. You can't expect to get the entire architecture right on the first attempt. – Robert Harvey Dec 7 '12 at 20:47
  • 2
    +1, and it's not counter to TDD. TDD begins when you start writing code, precisely when design ends. TDD can help you see what you missed in your design, allowing you to refactor the design and the implementation, and continue. – Steven Evers Dec 7 '12 at 20:53
  • 2
    Actually according to Bob (and I agree with him wholeheartedly) writing code is design as well. Having a high level architecture is definitely necessary but design doesn't end when you write your code. – Michael Brown Dec 7 '12 at 22:04
  • Really good answer that hits the nail on the head. I see so many people, both for and against TDD who seem to take "no big design up front" as "don't design at all, just code" when it's actually a strike against the insane waterfall design stages of old. Design is always a good time-investment and is crucial for the success of any non-trivial project. – sara May 7 '16 at 16:01

If you do TDD. You can't change the signature and behaviour without having driven it by tests. So the 30 tests that fail were either deleted in the process or changed/refactored along with the code. Or they are now obsolete, safe to delete.

You can't ignore the 30 times red in your red-green-refactor cycle?

Your tests should be refactored along side your production code. If you can afford to, re-run all tests after each change.

Don't be afraid to delete TDD tests. Some tests end up testing building blocks to get to a desired outcome. The desired outcome at a functional level is what counts. Tests around intermediate steps in the algorithm you chose/invented may or may not be of much value when there is more then one way to reach the outcome or you initially ran into a dead-end.

Sometimes you can create some decent integration tests, keep those and delete the rest. It somewhat depends whether you work inside out or top down and how large steps you take.

| improve this answer | |

As Robert Harvey just said, you are probably trying to use TDD for something that should be handled by a different conceptual tool (that is: "design" or "modelling").

Try to design (or "model") you system in a quite abstract ("general", "vague") way. For example, if you have to model a car, just have a car class with some vague method and field, like startEngine() and int seats. That is: describe what you want to expose to the public, not how you want to implement it. Try to expose just basic functionalities (read, write, start, stop, etc) and leave the client code elaborate on it (prepareMyScene(), killTheEnemy(), etc).

Write down your tests assuming this simple public interface.

Change the internal behaviour of your classes and methods whenever you need it.

If and when you need to change your public interface and your test suite, stop and think. Most likely this is a sign that there is something wrong in your API and in your design/modelling.

It is not unusual to change an API. Most systems at their 1.0 version explicitly warn the programmers/users against possible changes in their API. Despite this, a continuous, uncontrolled flow of API changes is a clear sign of bad (or totally missing) design.

BTW: You should usually just have a handful of tests per method. A method, by definition, should implement a clearly defined "action" on some kind of data. In a perfect world, this should be a single action that corresponds to a single test. In the real world it is not unusual (and not wrong) to have few different "versions" of the same action and few different corresponding tests. For sure, you should avoid to have 30 tests on the same method. This is a clear sign that the method tries to do too much (and its internal code is grown out of control).

| improve this answer | |

I look at it from the user's perspective. For example, if your APIs let me create a Person object with name and age, there had better be a Person(string name, int age) constructor and accessor methods for name and age. It's simple to create test cases for new Persons with and without a name and age.


| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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