Hacker News new | past | comments | ask | show | jobs | submit login

The point doesn't follow from the headline. The argument isn't that unit tests are bad per se, it's that good code has fewer testable abstractions, and thus requires fewer unit tests.

Which is basically isomorphic to "good code is tight code". Duh.




The argument is that good code [...] requires fewer unit tests

Great point, my mistake. Title and headline updated (was: Good code has few unit tests). I really wish it were "duh", but measuring code quality by "unit tests per line" [1] is frighteningly common, and seductive in its simplicity and intuition.

1. Not necessarily directly. It might be in the form of "I'll add more unit tests to improve this code" (unit tests either cover an interface or they don't; going in to 'add more unit tests' to 'improve' code means you didn't define a clear enough interface to begin with). Or "our open source project has more unit tests than another" (but I won't point fingers).


But "unit tests per line" is a good metric. Why? Because programmers (well, at least I) hate writing unit tests. If you have a high unit-tests-per-line ratio, then writing one fewer line of code will let you avoid writing several lines of unit tests.

The easiest way to bump your unit-tests-per-line metric is to delete lines of code. That's a positive thing, in my book.


Which isn't necessarily right, the whole "good code is tight code" thing.

If you're writing something simple, abstractions are probably just extra typing.

If you're building something that actually has multiple parts, those parts should be abstracted from each other.


If an abstraction requires extra typing, then it isn't really abstracting anything is it? Ie. you wouldn't use it if it wasn't going to benefit you.


Using an abstraction which increases the total amount of code could be worthwhile if it makes the difficult parts of your code simpler.

Pulling an example out of thin air, shorter total code:

    var counter = 0;

    function foo() {
        log("Incremented");
        counter++;
    }

    function bar() {
        log("Incremented");
        counter++;
    }

Code with an abstraction which makes foo and bar shorter:

    var counter = 0;
    
    function increment() {
        log("Incremented");
        counter++;
    }
    
    function foo() {
        increment();
    }

    function bar() {
        increment();
    }
Even longer code, but which encapsulates the counter variable, possibly making it easier to reason about:

    var Counter = (function ()
    {
        var counter = 0;

        return {
            increment : function() {
                log("Incremented");
                counter++;
            },
            current : function() {
                return counter;
            }
        };
    });

    function foo() {
        Counter.increment();
    }

    function bar() {
        Counter.increment();
    }


Well, if you abstract properly, you often wind up with fewer lines of code at the end, and certainly more predictable and easy-to-modify code. Am I really defending this? What's the opposing view, spaghetti code? :)


No, the opposing viewpoint to "more abstractions" is "better abstractions". ;-)

There are a surprisingly large number of programmers who learn "Oh yay, I can use abstractions to simplify this bit of code" and then turn that into "I think I'll make abstractions out of every bit of code". I was once one of them. In the process, they turn something that could've been a simple program, one that you could fit into your head, into an Enterprise Behemoth that you can't modify without implementing a half dozen interfaces and touching 30 files.

There's a cost to abstraction - it's (usually) extra code, it makes it harder to follow the logic of the program, and it makes your program less flexible in directions other than the intended use. So don't do it unless it actually gains you something in simpler code. Programs can become spaghetti-like through IFactoryFactories just as easily as they can become spaghetti-like through switch statements.

Also on Hacker News at the moment and fairly relevant:

http://coderoom.wordpress.com/2010/03/19/5-stages-of-program...


I said "properly", check out my other comments on this thread :)


But if you do test driven development then aren't you forced into testable abstractions?


I'm not forced to program at all. I do it because its most always held my interest. I've passed my 10,000 hour mark years ago. So for those that program well and do so by choice, why can't we spend our time focusing on defining good clean interfaces on the "real" code instead of the tests?


Well, don't get me wrong, I've seen my share of codebases with "Iface" and "Impl" classes (ew), and stupid, counterproductive unit tests that were done to check off boxes on a PM's checklist rather than improve stability. My favorite was a test to ensure that some static method somewhere returned the same hard coded string every time. The method was one line long, 'return "foo"; '.

But. If you've defined nice clean interfaces, and a particular module has complex behavior that will have to evolve over time, then adding tests to certify that that behavior is correct shouldn't be too much of a chore -- and they save your bacon when you need to change it and track any regressions afterwards.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: