Live data from Hacker News

Why TDD isn't crap

hillelwayne.com

121–130 of 171 posts

Re: Why TDD isn't crap

#121

I haven't shared this on HN but I saw this on another forum and thought this was a really fantastic example of what you can end up with, in the real world , when you are driven by tests. Real-world example: https://i.redd.it/lwin56fisdsz.png It doesn't look like a joke to me. It only works over integers so the code is absolutely correct. It also strikes me as the kind of convoluted logic that someone took a really, r…

Are there any proponents of serious testing or tdd that don't also promote code review? Why would tests make this code more likely? If anything, I am more confident that I can change it to be better if I have a test suite.

>Why would tests make this code more likely?

Code like this is basically only possible through testing, because the programmer doesn't understand why it is working: it says so in the comment, which I believe.

So if it weren't written against testing (manual or automatic) it simply couldn't be written like this.

There are a lot of really broken designs that "work". Testing gets you "working" code. Often you could do much better starting with correct code and then adding testing afterward - which I believe is not the essence of TDD. TDD is about driving the writing of code by tests, and I believe the example I shared is one of these bad outcomes. (There are others.)

Re: Why TDD isn't crap

#122

Earlier quoted context omitted.

I'm beginning to suspect that, in the context of these debates, "most static languages" is a roundabout way of saying, "Java", or other static languages with a similarly anemic type system. Also it's a roundabout way of describing the particular way that Java code tends to be structured. In one that has better support for generics (i.e., reification, contravariance and covariance) and some form of mixin, you generall…

> ...is a roundabout way of saying, "Java", or other static languages with a similarly anemic type system... I agree. In my experience, it's how the median developer writes code, though. Even in more flexible languages they'll reach for type hierarchies and abstract base classes. I've seen people create these things in languages like Lua and Javascript that don't really need them. I think TDD-like approaches make the…

Further agreement.

I haven't looked at a textbook on programming recently, but I'm worried that the standard is still to actively teach new developers to program this way, even though we've _known_ for decades that towering piles of subclasses invariably collapse under their own weight.

That said, I still wouldn't lay this tendency for damaged design at the feet of static typing in general. Not when some of the most vigorous arguments for static typing tend to come out of language communities that don't have subclassing in the first place (e.g., Haskell), and when (as you point out) similar mistakes are just as often made in dynamic languages. Dynamic languages are certainly more forgiving about poor design, but whether that's a good thing is yet another fun debate.

Re: Why TDD isn't crap

#123
post #35

Earlier quoted context omitted.

"having test play some part in how the code takes shape" That is exactly the point why I don't like TDD. I mean, there are enough constraints that shape the code, why should something artificial like tests shape it too? With mocking and everything you end up writing code for tests and not code for your problems.

Well, you write code for design flexibility. Testing just forces the issue and helps you explore possible design needs. For example, what if the database query you're using isn't suitable any more? Instead of designing around a User table, you start passing around User records. Then your design is more future proof in case you start getting Users from a service or in-memory cache instead of a database. > ...not code…

[deleted]

Re: Why TDD isn't crap

#124
post #8

Like the author, I subscribe to the less strict view that TDD isn't necessarily about writing the test first, but rather about having test play some part in how the code takes shape. Unlike the author, I absolutely believe that tests are about design. More specifically, they're about identifying coupling so that you can reduce it. The function being "awkward" to use is part of it, but code which is hard to test is al…

> code which is hard to test is almost always going to be hard to maintain This is a pointless equivalence to make. You're trying to suggest that code which is hard to test could be written differently, and better. This is not always true, and therefore useless to say. STUXNET was very difficult to test. Reports are that it required an entire Israeli nuclear facility as a test environment. Would you suggest that STUX…

Maybe it's only difficult to test end-to-end? I would assume there's code in there that's algorithm-like. Give it these inputs, it should return these outputs. If so, it should be possible to isolate that code and test it.

But, I dunno, I haven't looked at the source code. It might be very difficult to maintain.

Re: Why TDD isn't crap

#125
post #112

The only thing I disagree with in this article is the leeway given to supposedly legendary programmers who can somehow write bug-free code without tests, specifications, or an inkling of communication with others. First, it's probably not true. Linus Torvalds is not a legendary human being who can write critical systems without a single flaw. He relies on legions of human beings to carefully check and review every li…

Not disagreeing with your overall message, just want to point out something that bugs me and you're not the first one I heard saying something like this: "They provide theorems in the form of examples". This is incorrect. Theorems are deductive while test are experimental and this is a crucial difference in my opinion. To give and example: say we have our function isDAG :: Graph -> Bool, which determines whether a gr…

You're absolutely right. Thanks for pointing that out.

I was using theorem and proof as an analogy to illustrate the separation of specification and implementation.

A useful distinction as you get further along in writing formal specifications.

Update: typo.

Re: Why TDD isn't crap

#126
post #8

Like the author, I subscribe to the less strict view that TDD isn't necessarily about writing the test first, but rather about having test play some part in how the code takes shape. Unlike the author, I absolutely believe that tests are about design. More specifically, they're about identifying coupling so that you can reduce it. The function being "awkward" to use is part of it, but code which is hard to test is al…

How do you test your test if you don't write the test first? If you write the code, and then you write the test, then you haven't tested your test. You have no proof that your test will detect broken code. The only way to prove that your test can detect broken code is to run it against broken code. So you can write the code, then write the test, and then break the code and run the test. Or you could just write the te…

When I write a test after altering/adding code, I will generally stash the altered application code, and run the test I wrote on unaltered app code to confirm that it fails.

As long as one maintains decent version control discipline, you can be pretty confident that the test is valid with this technique. (It does get a bit more difficult when there are schema changes, but then it also encourages good discipline and vigilance when you're making schema changes ;)

Re: Why TDD isn't crap

#127
post #8

Like the author, I subscribe to the less strict view that TDD isn't necessarily about writing the test first, but rather about having test play some part in how the code takes shape. Unlike the author, I absolutely believe that tests are about design. More specifically, they're about identifying coupling so that you can reduce it. The function being "awkward" to use is part of it, but code which is hard to test is al…

I think TDD (or rather, unit tests):

1) ... over-emphasise the importance of reduced coupling, and can actually increase the chances of integration failures. Why? Because components are tested in isolation rather than together, but are still considered "tested" - especially when using stubs and mocks. I've seldom seen mocking used well, it almost always over-specifies implementations.

2) ... increase the tested "surface area" of the code, making it much more costly to modify in a way that changes the "surface area". When two bits that normally only ever face one another are tested on both sides, you now have 4 places to update when you move functionality between them instead of 2. And that's just for a small refactoring.

3) ... encourage a spurious modularity that increases the overall conceptual complexity of the solution. When every dependency needs to be replaced for testing, they become parameters, one way or another; and thus the code becomes over-generalized and over-abstracted, more removed from the work it's doing and more concerned with the bureaucracy of communication and coordination. It's this spurious modularity that's the cause of Enterprise Java FactoryFactories and friends.

That sounds quite negative on unit tests, and actually I'm not negative at all. I think code can be modified in more than one way to accommodate tests; poorly, by introducing single-implementation interfaces everywhere; or better, by reducing the number of types of interfaces, converting control flow into data flow, and generally adopting more functional and higher-order compositional architectural patterns.

It's hard to get more specific without talking about examples, though, so I'll stop here.

Re: Why TDD isn't crap

#128

Earlier quoted context omitted.

How do you test your test if you don't write the test first? If you write the code, and then you write the test, then you haven't tested your test. You have no proof that your test will detect broken code. The only way to prove that your test can detect broken code is to run it against broken code. So you can write the code, then write the test, and then break the code and run the test. Or you could just write the te…

Although on the other hand, writing your test first with no code and having it fail isn't really telling you much about the test. It just shows that the test handles the least interesting case (the case where there exists no code). You still don't know that your test is correct when you get it passing. You just know that it is correct when no code exists. For example, TestSort(ISorter sorter) { var input = { 1, 2, 3,…

If someone gave me that test, I'd implement the code as "return input;"

Forget that you, a human, thinks you know what an ISorter should do. What do the tests demand that you do?

Doing TDD, the goal, when coding, is to write the minimal amount of broken code that makes the test pass. If you passed me {4,3,2,1}, then my code would be "return {1,2,3,4};". If you wrote a second test that passed me {6,5,4,3}, my code would be "return (input[0]==4)?{1,2,3,4}:{3,4,5,6};". You've got to write a test that makes me actually code what you want. I use this adversarial approach even when I'm writing both the test and the code.

Usually the first test I write if I'm starting with a blank slate, is to pass null and check that I get a NullPointerException. That gets the class created. After that, its got to be randomly generated data.

Re: Why TDD isn't crap

#129
post #6

TDD isn't bad at all for a mature product where you have clear requirements for additional feature development and granular developer tasks. I've just seen a lot of it where a product is still in broad strokes development and developers get stuck between whether having tests written based on early assumptions are correct and the code should conform to those, or whether new ways of thinking that invalidates the early…

Tests allow you to easily refactor implementation and allow you to add features without worrying as much about breaking the existing interface.

However, they're terrible if you want to refactor your design and correct problems with the interface itself. Tests freeze the interface. This is really good for whole classes of problems but terrible for other classes of problems.

The debate is going to continue endlessly as those on either side are looking at it from entirely different frames of reference.

Re: Why TDD isn't crap

#130
post #126

Earlier quoted context omitted.

How do you test your test if you don't write the test first? If you write the code, and then you write the test, then you haven't tested your test. You have no proof that your test will detect broken code. The only way to prove that your test can detect broken code is to run it against broken code. So you can write the code, then write the test, and then break the code and run the test. Or you could just write the te…

When I write a test after altering/adding code, I will generally stash the altered application code, and run the test I wrote on unaltered app code to confirm that it fails. As long as one maintains decent version control discipline, you can be pretty confident that the test is valid with this technique. (It does get a bit more difficult when there are schema changes, but then it also encourages good discipline and v…

That's good. I suggest that you're doing unnecessary work (having to stash and unstash) when you had "broken" code at first, but at least you are testing your tests, so well done.

In my experience coaching teams on TDD, its quite easy, when writing code first, to add more code than is needed to solve the problem. And then only the problem is tested for. Meaning that there is code that is not tested. Maybe an if statement where a branch is never taken. Strict TDD means you don't write any code without a failing test, and you write the minimal code needed to make it pass.

Now quite often, a programmer has no idea how to tackle a problem, so I advise that they just write something that works. Just have a go and get something working. Most programmers (if they test at all) will then just go and write a test for it, usually for happy path. And that usually results in code with bugs. I know this because many times the programmer has done as I suggested and commented out their code, and reimplemented from scratch using TDD, and doing TDD finds a bug in the original that nobody saw.

Post reply on HN