Patterns
There are different approaches in how to test-code-refactor a complex class hierarchy. Some people are in favor of the nested approach, while others are not. Each of them pose advantages and disadvantages. When is each applicable?
For example, let's say you have to code class A, which you THINK is going to need classes B, C, D, and E. Here are some choices:
Note: test X implies testing class X and having it pass 100% Observation: Maybe THINKING you'll need classes B-E is the mistake?
a) Serial approach
write test E, write E, test E write test D, write D, test D write test C, write C, test C write test B, write B, test B write test A, write A, test AAdvantages: ???
Disadvantages: Violates YAGNI How does this violate YAGNI? It violates YAGNI because you THINK you need classes B-E, but you should not implement them until another class actually needs them.
b) Nested approach
write test A, write A write test B, write B write test C, write C write test D, write D write test E, write E, test E test D test C test B test AAdvantages: ???
Disadvantages: Time between implementation and testing could be too extensive, violates YAGNI, nesting is hard to manage
c) Nested approach with partial implementation
write test A, write A (just enough to pass test), test A write test B, write B (just enough to pass test), test B write test C, write C (just enough to pass test), test C write test D, write D, (just enough to pass test) write test E, write E (fully), test E write D (fully), test D write C (fully), test C write B (fully), test B write A (fully), test AAdvantages: ???
Disadvantages: Violates YAGNI, nesting is hard to manage
d) Partial nesting approach
write test A, write A write test B, write B, test B write test C, write C, test C write test D, write D, test D write test E, write E, test E test AAdvantages: ???
Disadvantages: Dependencies between classes are not tested (Remember class B calls class C, where is this tested?), violates YAGNI.
e) Solution proposed by HagbardCeline (http://groups.yahoo.com/group/extremeprogramming/message/23907)
Write test A. Write enough of A to know how I call B. Write test B. Write enough of B to know how I call C. Write test C. Write enough of C to know how I call D. Write test D. Write enough of D to know how I call E. Write test E. Write E. While Test E fails, continue working on E. Write the rest of D. While test D fails, continue working on D. Write the rest of C. While test C fails, continue working on C. Write the rest of B. While test B fails, continue working on B. Write the rest of A. While test A fails, continue working on A.Advantages: The A-E are all written test-first and all pass at 100% before I move on.
Disadvantages: Nesting is hard to manage.
If you only THINK you need B, C, D and E then write A with the parts you think you need represented as attributes or methods of A. (I assume that because thinking about A makes you think about B, C, D and E they, or some parts of them, fall "inside" the "A Space" in some way). When A becomes unwieldy or some good reason appears to factor out one of the MAYBE classes, do it and write tests for the new class and change the tests for the refactored A. Until then you don't have the design information to worry about writing them. If you do have it, then you KNOW you will need them all and the problem is the same as anytime you know you need to write more than one related class. (Interesting, what is clearly implied is that the tests themselves are in some way part of the "code" of the application and hold design information about it.) -- KennethTyler?
Kenneth, to summarize, is this what you mean:
f) Solution proposed by IljaPreuss? (http://groups.yahoo.com/group/extremeprogramming/message/15771)
write test A, write A, test A refactor A to A, B, C, D & EAdvantages: ???
Disadvantages: ???
There seem to be two forces at play here:
What KennethTyler? describes satisfies both forces but is not exactly equivalent to the IljaPreub? approach.
while task A is not completed
write test for task A write enough to pass test if classes becomes unwieldly refactorThe main point is that the refactoring doesn't all happen at the end and the initial tests are more for the intended task than the implemented classes. I also add a bunch of white box tests at the end to convince myself that the implemented classes work correctly but those aren't TestFirstByIntention? kind of tests. -- JasonYip
I call this one GrowThenSplit?. I describe it a bit at http://www.xprogramming.com/xpmag/tfd2.htm. -- MichaelFeathers
Egroups Threads:
http://groups.yahoo.com/group/extremeprogramming/message/15342 http://groups.yahoo.com/group/extremeprogramming/message/15370 http://groups.yahoo.com/group/extremeprogramming/message/16139 http://groups.yahoo.com/group/extremeprogramming/message/16164 http://groups.yahoo.com/group/extremeprogramming/message/16171 http://groups.yahoo.com/group/extremeprogramming/message/16181