Naive Tests Don't Help.
It may sound obvious to some and blasphemous to others, but you can not really start TDD with a blank sheet. If you think you have done so, you're underestimating how much you already know or you're underestimating how much time you are wasting.
The idea of sitting down and writing tests for a new component in blissful isolation is intellectually appealing, but it loses. There have been a few times now that I have intentionally ignored existing code and started "pure" test-driven development of a new component. The tests were easily written, ran well, proved that my functionality was in place, and were completely wrong and useless.
The problem is that actual system (or api set) would not use the components the _way_ that the tests use them. One example was not knowing that a component required two-phase construction. This led to me putting functionality into a multi-argument constructor. It tested well, but didn't work. This kind of naivete leads to one reimplementing the tests and refactoring the components.
Instead, the tests have to be guided by a fairly intimate knowledge of the way the system will actually use the proposed module. Code doesn't live in a vacuum, and one has to understand the operating philosophy and interface points of the existing design(s), TDD or not.
!commentForm
but doesn't the concept of Acceptance Tests exist for the very purpose of avoiding the dilemma that you describe? My understanding is that you write an acceptance test first, which acts as the scaffolding which channel your TDD-developed code "vines" in the right direction.
It depends on whether the class your are creating has "plug into" a framework, or if it just drives other code and that other code doesn't require your class to behave in any specific way.
I may create "pure model" objects that don't plug into anything, freely doing any design that I want via TDD, and then create Presenter and View with "constrained TDD" to make them usable from the user interface.
I may create "pure model" objects that don't plug into anything, freely doing any design that I want via TDD, and then create Presenter and View with "constrained TDD" to make them usable from the user interface.
Well, it happened again today. The story was simple to execute. There is a document format and the document format specifies certain bytes in certain positions. Easy enough. If the data is variable at all, it's a variable. If it's a hard requirement that the first two bytes of the record are "06" then that's a format detail, and the simplest thing that works is to leave it a constant in the code.
However, the top-down solution ended in arguments and trouble. Someone previously decided that all the constants would be stored in the database, and by having constants in the code for the document we were duplicating information. The database wins, I'm sure.
But here we go again, yes? The top-down doesn't necessarly mesh with the bottom it has to reach. You can't start each feature top-down and expect it to really work. You have to know the bottom and how you'll fit the foundation. You have to read the code, and have enough product knowledge to choose tests that will work with the previously written stuff.
That means that you have to do code exploration and design before starting TDD on existing systems. Lack of product knowledge is not something that you can overcome with this particular technique. TDD doesn't help, but pairing might (if your partner is knowledgeable in the right bits of existing code).
However, the top-down solution ended in arguments and trouble. Someone previously decided that all the constants would be stored in the database, and by having constants in the code for the document we were duplicating information. The database wins, I'm sure.
But here we go again, yes? The top-down doesn't necessarly mesh with the bottom it has to reach. You can't start each feature top-down and expect it to really work. You have to know the bottom and how you'll fit the foundation. You have to read the code, and have enough product knowledge to choose tests that will work with the previously written stuff.
That means that you have to do code exploration and design before starting TDD on existing systems. Lack of product knowledge is not something that you can overcome with this particular technique. TDD doesn't help, but pairing might (if your partner is knowledgeable in the right bits of existing code).
FWIW, the triumph of the database on this story (Tim and I were pairing) was prematurely reported. Turns out there are only three or four actual variables, and they come from objects already in memory. Everything else is hardcoded now. Using the db was YAGNI. The story's not done yet, but the sort-of-top-down-TDD approach has been working fine so far.
Add Child Page to NaiveTestsDontHelp