Live data from Hacker News

The Failures of "Intro to TDD"

blog.testdouble.com

71–80 of 85 posts

Re: The Failures of "Intro to TDD"

#71
post #70

Earlier quoted context omitted.

Are you asking about unit tests or acceptance tests? I suppose what I’m really asking is how you would go from not having software to having software that does those things, using TDD. I think in practice its fail-pass-refactor cycle is normally applied at the level of unit tests, but in any case, how would using TDD help to drive a good design, to ensure testability, or otherwise, in that kind of situation? (I’m ask…

I think I mostly agree with your larger point, but I'm not in love with your examples. The Mandelbrot set does consist of readily verifiable discrete data points, after all. I don't have any problem imagining myself developing a Mandelbrot set program using TDD. A great example for your point (which might have been what you were getting at with the audio thing) is a test for creating files in a lossy audio format. Th…

The Mandelbrot set does consist of readily verifiable discrete data points, after all.

Indeed it does, but in order to verify them I see only two options.

One is that you have to choose test cases where the answer is trivially determined. However, with this strategy, it seems you must ultimately rely on the refactoring step to magically convert your implementation to support the general case, so the hard part isn’t really test-driven at all.

The other is that you verify non-trivial results. However, to do so you must inevitably reimplement the relevant mathematics one way or another to support your tests. Of course, if you could do that reliably, then you wouldn’t need the tests in the first place.

This isn’t to say that writing multiple independent implementations in parallel is a bad thing for testing purposes. If you do that and then run a statistical test comparing their outputs for a large sample of inputs, a perfect match will provide some confidence that you did at least implement the same thing each time, so it is likely that all versions correctly implement the spec. (Whether the spec itself is correct is another question, but then we’re getting into verification vs. validation, a different issue.) However, again for a simple example like rendering a fractal, you could do that by reimplementing the entire algorithm as a whole, without any of the overheads that TDD might impose.

I don't have any problem imagining myself developing a Mandelbrot set program using TDD.

I’m genuinely curious about how you’d see that going.

Re: The Failures of "Intro to TDD"

#72
post #14

OK but after you "Fake It Until You Make It" and you have to add a new feature to that class structure, aren't you just going to start over with all the failures he brings up? --------- 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) Y…

"1) Sometimes you end testing and writing a leaf that you you don't end up using/needing."

So what? Just delete it. Your version control system should have a record of what it was if you end up needing to go back to it.

Re: The Failures of "Intro to TDD"

#73
post #32

I wonder about these workshops (even asked Uncle Bob Martin about them in a recent thread). I can't shake the feeling they are the exact opposite of agility (obviously, he is better qualified than me to judge that). Their limited time schedules, which is essentially a bound over the amount of contact between the client and the supplier, seems analogous to the infamous "requirements document". Also, there doesn't appe…

Uncle Bob responds: https://news.ycombinator.com/item?id=7139961

Re: The Failures of "Intro to TDD"

#74
post #66

The comments section today looks like a support group for beginners/intermediates who struggled with TDD and gave up, and so want to explain why it's all bunk. I get this. I am not a great programmer. I'm self taught like a lot of you. I had tremendous difficulty grokking TDD and for the longest time I'd start, give up, build without it. But, I'm here as a you-can-do-it-to. You might not think you want to but I'm so…

"If you're struggling to write tests, and they're hard to write, messy, take a lot of setup, are slow to run, too tightly coupled etc. you have a design problem." This is my problem exactly, and I wouldn't say I have a design problem. My application is a Django app that return complex database query results. Creating the fixtures for ALL of the edge cases would take significantly longer than writing the code. At this…

sure... it can be more broadly stated "you have a design problem and/or you're testing the wrong things"

Re: The Failures of "Intro to TDD"

#75
post #70

Earlier quoted context omitted.

I think I mostly agree with your larger point, but I'm not in love with your examples. The Mandelbrot set does consist of readily verifiable discrete data points, after all. I don't have any problem imagining myself developing a Mandelbrot set program using TDD. A great example for your point (which might have been what you were getting at with the audio thing) is a test for creating files in a lossy audio format. Th…

The Mandelbrot set does consist of readily verifiable discrete data points, after all. Indeed it does, but in order to verify them I see only two options. One is that you have to choose test cases where the answer is trivially determined. However, with this strategy, it seems you must ultimately rely on the refactoring step to magically convert your implementation to support the general case, so the hard part isn’t r…

I kind of wish I had the time to actually do it right now and see how it works. But here's how I imagine it going:

1) Establish tests for the is-in-set function. You're absolutely right that the most obvious way to do this meaningfully is to reimplement the function. A better approach would be to find some way to leverage an existing "known good" implementation for the test. Maybe a graphics file of the Mandelbrot set we can test against?

2) Establish tests that given an arbitrary (and quick!) is-in-set function, we write out the correct chunk of graphics (file?) for it.

3) Profit.

Observations: 1) I absolutely would NOT do the "write a test; write just enough code to pass that test; write another test..." thing for this. My strong inclination would be to write a fairly complete set of tests for is-in-set, and then focus on making that function work.

2) There's really no significant design going on here. I'd be using the exact same overall design I used for my first Mandelbrot program, back in the early 90s. (And of course, that design is dead obvious.)

In my mind, the world of software breaks down something like this: 1) Exhaustive tests are easy to write. 2) Tests are easy to write. 3) Tests are a pain to write. 4) Tests are incredibly hard to write. 5) Tests are impossible to write.

I think it's pretty telling that when TDD people talk about tests that are hard to write, they mean easy tests in hard to get at areas of your code. I've never heard one discuss what to do if the actual computations are hard to verify (ie 4 & 5 above) and when I've brought it up to them the typical response is "Wow, guess it sucks to be you."

Re: The Failures of "Intro to TDD"

#76
post #70

Earlier quoted context omitted.

I think I mostly agree with your larger point, but I'm not in love with your examples. The Mandelbrot set does consist of readily verifiable discrete data points, after all. I don't have any problem imagining myself developing a Mandelbrot set program using TDD. A great example for your point (which might have been what you were getting at with the audio thing) is a test for creating files in a lossy audio format. Th…

The Mandelbrot set does consist of readily verifiable discrete data points, after all. Indeed it does, but in order to verify them I see only two options. One is that you have to choose test cases where the answer is trivially determined. However, with this strategy, it seems you must ultimately rely on the refactoring step to magically convert your implementation to support the general case, so the hard part isn’t r…

Whether the spec itself is correct is another question, but then we’re getting into verification vs. validation, a different issue.

I think you get to the nub of it here. TDD lets you develop a spec that is consistent with requirements (the subset so far implemented) and the code at all times.

Writing a comprehensive suite of tests before any production code is like writing a complete spec without any clue as to its applicability. Writing tests afterward would be analogous to writing a spec for an already shipped product.

Tests work both ways in TDD: you are checking both that the code behaves as intended and that your expected behavior is reasonable. If it were only about the former it wouldn't be very valuable.

Re: The Failures of "Intro to TDD"

#77
post #41
post #18

Earlier quoted context omitted.

The trouble with abandoning symmetrical unit tests is that: * 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…

Unless I'm missing something, wouldn't the child dependency be enough to prevent the unit from being dropped into another library or app? That's a good point you bring up about knowing where to "test drive" the changes from, though usually on the apps I've worked on, they've been small enough that the relevant integration test could be found without much detective work. I guess I haven't been involved in too many 2-3…

The author is stating that the child dependency cannot be extracted to another library or app. If it is extracted, it is untested, because the only tests wrapping the child dependency are actually testing the child's original parent. (Which is likely to not exist in whatever other library/app to which the child component is moved.) And then, to retroactively add tests to the child component in order to facilitate moving it to another library, is painful.

Having symmetrical tests enable components to be easier moved to other libraries/apps because the test can move with the unit under test.

Re: The Failures of "Intro to TDD"

#78

Those guys must really hate their readers. That crappy web site is not zoomable! In the 21st century? In the era of “responsive web design”? Mega fail. Did they use TDD?

What's your definition of zoomable? I'm able to adjust text size just fine. More details on your specific issue? If you mean layout, it is responsive. The text column narrows and the images never exceed 100% width.

Re: The Failures of "Intro to TDD"

#79

What about tools like Typemock? How does that fit in?

Tools like Typemock helps you make bad decisions that you will regret later on...

Isolating things is very important to make it easier to test, and lower the risk for tests to break when you change other parts of the system. Some times isolating one part from another is hard work. Typemock makes it easier, but in the same time it ties you closer to the part that you are trying to isolate from.

e.g. a database. You want to test something that eventually should store something in a database. You can either make a thin layer abstracting away your database so that you can test the functionality without depending on the database, or you can make a tighter coupling to the database, and use tools like typemock to get rid of it in test mode. If you want to change the way you store data, you now have production code tightly coupled to the current storage strategy AND tests tightly coupled to the current storage strategy...

Typemock can be of great help some times, but really you should strive to find better designs instead.

Re: The Failures of "Intro to TDD"

#80
post #75

Earlier quoted context omitted.

The Mandelbrot set does consist of readily verifiable discrete data points, after all. Indeed it does, but in order to verify them I see only two options. One is that you have to choose test cases where the answer is trivially determined. However, with this strategy, it seems you must ultimately rely on the refactoring step to magically convert your implementation to support the general case, so the hard part isn’t r…

I kind of wish I had the time to actually do it right now and see how it works. But here's how I imagine it going: 1) Establish tests for the is-in-set function. You're absolutely right that the most obvious way to do this meaningfully is to reimplement the function. A better approach would be to find some way to leverage an existing "known good" implementation for the test. Maybe a graphics file of the Mandelbrot se…

1) I absolutely would NOT do the "write a test; write just enough code to pass that test; write another test..." thing for this. My strong inclination would be to write a fairly complete set of tests for is-in-set, and then focus on making that function work.

The latter is what I’d expect most developers who like a test-first approach to do. I don’t see anything wrong with it, either. I just don’t think it’s the same as what TDD advocates are promoting.

I think it's pretty telling that when TDD people talk about tests that are hard to write, they mean easy tests in hard to get at areas of your code. I've never heard one discuss what to do if the actual computations are hard to verify (ie 4 & 5 above) and when I've brought it up to them the typical response is "Wow, guess it sucks to be you."

Indeed. At this point, I’m openly sceptical of TDD advocacy and consider much of it to be somewhere between well-intentioned naïveté and snake oil. There’s nothing wrong with automated unit testing, nor with writing those unit tests before/with the implementation rather than afterwards. Many projects benefit from these techniques. But TDD implies much more than that, and it’s the extra parts — or rather, the idea that the extra parts are universally applicable and superior to other methods — that I tend to challenge.

Thus I object to the original suggestion in this thread, which was that a developer probably doesn’t know what they are doing just because they can’t articulate a test case according to the critic’s preferred rules. I think those rules are inadequate for many of the real world problems that software developers work on.

Post reply on HN