BDD / TDD criticized

,

Interesting read: http://rbcs-us.com/documents/Why-Most-Unit-Testing-is-Waste.pdf
Reminds me of Rich Hickey, who said in an interview:“Each of us needs to assess how best to spend our time in order to maximize our results, both in quantity and quality. If people think that spending fifty percent of their time writing tests maximizes their results—okay for them. I’m sure that’s not true for me—I’d rather spend that time thinking about my problem. I’m certain that, for me, this produces better solutions, with fewer defects, than any other use of my time. A bad design with a complete test suite is still a bad design.” (http://www.codequarterly.com/2011/rich-hickey/)

20 Likes

I will mostly agree :). But that said that depends heavily from project to project. I found functional, i.e. tests from user perspective or tests that do some real operation on database more useful in long run than unit tests.

That said, there are always parts of the system where you need unit tests. Price calculation. Account balance. Cooling control mechanism for nuclear reactor ;). These sort of things.

In the long run a lot of unit tests impede your ability to react quickly or even refactor code. I don’t mean functional tests that are extremaly useful when refactoring code and ensuring stuff works. But the tests that go and inspect your code/data, you basically need to make changes in two or places, when you change the internal code.

Other thing is the project characteristics. When you go full agile, in terms that you do change the features already implemented a lot, and some clients do that, I don’t mind if we have less tests.

13 Likes

How do you refactor code without unit test? Also it easy for someone start with new code by looking on the tests.
I know that in static typed languages you need less test -> some work is doing compiler for you. In dynamic languages like Clojure, Elixir is a bit harder.

Functional tests are slow. Unit tests are fast. For example in Scala you can turn on fire all unit test on code change.
So you are typing and you have real time feedback. This is coooool. :upside_down:

3 Likes

@mkunikow i feel like this is an oversimplification of both the benefits and costs of unit testing and immediate feedback. I don’t think the parent article was dismissing unit tests out of hand; rather, he was elaborating on the hidden costs of unit tests.

You mentioned refactoring. From my perspective, unit tests actively prevent refactoring, if the intention is that a refactoring does not break any existing interfaces. What is helpful in refactoring is to have a set of tests that define the boundary of the refactoring. Its a proactive statement that this is the interface we’re protecting, and any client of the interface can be assured that it will not change. Under the covers, all bets are off and in fact a refactoring probably will break unit tests below that functional interface.

Likewise, immediate feedback is not helpful when you are working on code you know is broken, in flux, or incomplete. Its like the proverbial child in the back seat of one’s car on a long road trip, incessantly asking “Are we there yet? Are we there yet? Are we there yet?” No, kid, we’re not there yet and if you ask me one more time I’m going to pull this car over…

Also, while statically typed languages do protect you from careless or inadvertent typing mistakes, they do nothing to ensure that you’ve got the business or domain logic correct. BEAM projects have the benefit of dialyzer, which provides most of the benefits of static typing in an accessible package.

All of the above isn’t to discount the value of unit tests while developing code, or having a test suite that runs very quickly so as to provide real-time feedback to a developer, on request. I very frequently write tests whose purpose is not to be long-lived. They are there to provide a scaffold to build code around, but they provide no real value in the longer term. Sometimes they’re even experimental in nature, when i’m trying to get a feel for how an interface should work. In either case, I try to delete them once I’ve discovered the true boundaries of the interface and have written tests that define it and cover the edge cases. Of course, this is what i’ve found works for me, to each his own and YMMV.

9 Likes

I think the point that often gets missed in these debates is that tests are code. And as such they need just as much if not more attention to deleting cruft and maintenance.

The only thing worse than no tests is bad tests.

7 Likes

Ok I agree. There is always cost of maintaining test. There is always cost of maintaining code. The problem is to find optimal balance :slight_smile:

6 Likes

Had some talks for contractor jobs. It surprises me how often I hear things like “we only deliver software with 100% coverage”. Ruby heritage? Sound like bureaucracy to me. Joel Spolsky had a nice talk on the subject also: From Podcast 38 – Joel on Software

the worst thing that happens is that you get people that just stop thinking about what
they’re doing. “This is the principle, to always write unit tests, so I’m always going
to write unit tests,” and then they’re just not thinking about how they’re spending
their time, and they wind up wasting a lot of it.

1 Like

Having 100% code coverage does not ensure that your application is robust. You can write bad tests or even worse writing a test that returns true and yes, you’ll have 100% coverage but without significant tests… So having 100% code coverage as a goal is simply “stupid”.

4 Likes

Actually, there are languages like Idris that can statically enforce state transitions, for example, by having dependent types. What’s correct is that most statically typed languages do not have dependent types, but the ones that do are able to reason about things like state transitions at compile-time.

While I agree that static typing as it stands in most languages doesn’t solve most issues it should be said that “statically typed” covers more area than what most languages of today cover and the solution, in my mind, isn’t to say “Since types don’t solve everything, let’s not use them”, but instead to push in the opposite direction. Put more logic in your type systems, allow more checking of everything at compile-time to make the compiler more of a friend that guides you.

This brings a lot of benefits which will be obvious when you start using statically checked languages; you get lots and lots of helpful error messages while writing your code. This can be combined with outside services that can tell you when you can refactor things to be more simple, etc.

6 Likes

Just to be on point : it means you have a sound and really powerful type system.

Outside of Idris, maybe Haskell and let say Scala and OCaml for some things, i don’t think there are a tons of languages that have that and are “production ready” out there sadly.

2 Likes

Old post, but gotta comment. No, dialyzer does not provide most of the benefits of static typing. Dialyzer is awesome yes, but a properly typed system still saves me from a ton of my own things that would otherwise be bugs.

This is where things like Quickcheck and AFL-like fuzzers come in handy, don’t worry about your tests then, just define how to access the functions and some expected returns and let them go. They will catch a lot more things than normal tests will, but having some functional tests to enforce good returns is still needed. Unit tests… eh, very dependent…

Haskell, Scala, and OCaml do not have built-in dependent types, however, in all of those you can build your representations of the data to make it impossible to get in to a bad state. This does take a bit more thinking than base programming, but it is something that I always encourage. GADT’s supplied by both Haskell and OCaml significantly help with the complexity of those as well.

4 Likes

That was my point :slight_smile:

1 Like

I am not sure if anyone has used Jest for JS testing but it has handy snapshot feature that makes creating tests almost free (for those who are not into TDD). Maybe adopting something like that would be useful for API testing for example.

In my experience, code without tests is absolutely worthless. Can’t understand it, can’t refactor it, can’t properly fix bugs on it.

3 Likes

TDD and writing tests are orthogonal :slight_smile:
There are fairly vocal critics of TDD and or unit tests who one would struggle to call not experienced DHH, Rich Hickey, Donald Knuth etc. As far as I remember @chrismccord is also in Test-After camp :slight_smile:

4 Likes

I’m in the test-after camp also. Well architected, maintainable software (answering user requirements), performance tuned when needed, comes first, writing test-driven and striving for maximum coverage is something else. For detailed critiques the link in my first mail is a good one http://rbcs-us.com/documents/Why-Most-Unit-Testing-is-Waste.pdf. Calling Rich Hickey not experienced enough to be taken seriously is of course worth a good laugh. James O Coplien (writer of that pdf) is not that obtuse either.
There is a follow up of the first article I sent (in the first post) by the way: http://rbcs-us.com/documents/Segue.pdf

1 Like

TDD means that tests govern how you move forward. Mostly no one is arguing that you shouldn’t have tests, but it’s a matter of tests coming first or later.

5 Likes

But how is going is it supposed “govern how you move forward” if they come after you have already moved? Besides, I have never heard of a successful implementation (or even formalization) of Test Second.

My opinion is if you write test, write them first. If you don’t write test, I will not consider your code in projects I am responsible for.

1 Like

+ striving for 100% coverage will not return investment. Test smart, don’t follow a dogma.

3 Likes

You are definitely using code that was not done as TDD in projects you are responsible for :slight_smile:
Git
Linux/Solaris/BSD/Windows
it would be pointless to continue the list as it would be very long. If you list your production stack I would bet that at least 90% of the code by LOC count was not done as TDD.

8 Likes