I agree with the general approach suggested in the article (in tests, write/assume the code you wish you had). But one detail ran counter to my personal practice. I don't believe that "symmetrical" unit tests are a worthy goal. I believe in testing units of behavior, whether or not they correspond to a method/class. Symmetry leads to brittleness. I refactor as much as possible into private methods, but I leave my tes…
The Failures of "Intro to TDD"
11–20 of 85 posts
Re: The Failures of "Intro to TDD"
#12I agree with the general approach suggested in the article (in tests, write/assume the code you wish you had). But one detail ran counter to my personal practice. I don't believe that "symmetrical" unit tests are a worthy goal. I believe in testing units of behavior, whether or not they correspond to a method/class. Symmetry leads to brittleness. I refactor as much as possible into private methods, but I leave my tes…
I understand the concern, but I value consistency and discoverability, so symmetry of thing-being-tested to test itself is (so far) the best way I've found to make sure it's dreadfully obvious where a given unit's test is. This approach is not concerned with brittleness or being coupled to the implementation because each unit is so small that it's easier to trash the object and its test when requirements change than…
Personally, I like my tests to be pretty clearly about the behavior of the contract, and not the implementation, which is hard when you require every method have a test.
I'd also be concerned that other team members are reluctant to delete tests - as this is a dysfunction I see often, and try to counteract with varying degrees of success.
Re: The Failures of "Intro to TDD"
#13When I'm teaching TDD, the kata I have everyone go through is a simple order system.
The requirements are something like:
A user can order a case of soda
The user should have their credit card charged
The user should get an email when the card is charged
The user should get an email when their order ships
If the credit card is denied, they should see an error message
(etc....)
This way they can think about abstracting out dependencies, an IEmailService, a ICreditCardService, etc. There are no dependencies for a Roman Numeral converter.
Re: The Failures of "Intro to TDD"
#14---------
I haven't designed code the way he's advocating, but I have attempted TDD by starting with the leaves first. Here are the downsides to that:
1) Sometimes you end testing and writing a leaf that you you don't end up using/needing.
2) You realize you need a parameter you didn't anticipate. EG: "Obviously this patient report needs the Patient object. Oh crap I forgot that there's a requirement to print the user's name on the report. Now I've got get that User object and pass it all the way through".
Maybe these experiences aren't relevant. As I said, I haven't tried to "Fake It Until You Make It".
Re: The Failures of "Intro to TDD"
#15The more I try to explain TDD, the more I realize that some of my favorite concepts, like the ability to mock functionality of an external process because the details of that process should be irrelevant...is just beyond the grasp of most beginners. That is, I thought/hoped that TDD would necessarily force them into good orthogonal design, because it does so for me...but it seems like they have to have a good grasp o…
IMHO junior programmers tend to think that over specifying a design helps them, only a master can recognize the brilliance of something like SMTP/REST/JSON over X400/SOAP/XML. TDD just helps them over specify their bad designs.
That said TDD is a wonderful tool in the hands of a master. It's like photography, a $10,000 camera won't help you solve your composition problems. Tech can help ensure Ansel Adams doesn't take a photo with the wrong focus, but a properly focused poorly composed image does not a masterpiece make.
Re: The Failures of "Intro to TDD"
#16Thank you! Details of this post aside, this gave me an Aha! moment and I feel like I'm finally leaving the WTF mountain.
Re: The Failures of "Intro to TDD"
#17Re: The Failures of "Intro to TDD"
#18Might one of the problems be that we place too much importance on the "symmetrical" unit test. In your example the child code is still covered when it is extracted from the parent. As a developer that often prefers tests at the functional level, the primary benefit of tests for me is to get faster feedback while I am developing.
* The unit is no longer portable and can't be pulled from the context it was first used in (e.g. into a library or another app) without becoming untested. And adding characterization testing later is usually more expensive * A developer who needs to make a change to that unit needs to know where to "test drive" that change from, which requires that they know where to look for the parent's test that uses it. That's hard enough but it completely falls over when the unit is used in two, three, or more places. Now a bunch of tests have to be redesigned and none of them are easy to find. * Integrated unit tests like this lead to superlinear build duration growth b/c they each get slower as the system gets bigger. This really trips teams up in year 2 or 3 of a system.
Re: The Failures of "Intro to TDD"
#19The approach outlined actually makes much more sense without OO. I guess the WTF comes from forcing yourself into a world of "MoneyFinder", "InvoiceFetcher", etc. Makes it look a lot more complicated and prone to error than it is, because you're now supposed to mock objects that may have internal state. Otherwise it's the usual top-down approach with stubs.
However, in a classical language it's easier to organize stuff into classes and for the purpose of a post like this one it's easier to convey. But you're dead on.
Re: The Failures of "Intro to TDD"
#20The approach outlined actually makes much more sense without OO. I guess the WTF comes from forcing yourself into a world of "MoneyFinder", "InvoiceFetcher", etc. Makes it look a lot more complicated and prone to error than it is, because you're now supposed to mock objects that may have internal state. Otherwise it's the usual top-down approach with stubs.