My reaction to “Is TDD Dead?”

Whatever your stance on the merits or pitfalls of Test Driven Development, I think it’s worthwhile and educational to pay attention to a discussion that’s going on lately.

Testing is crucial. But is unit test focused TDD the right path?

I care about the conversation about TDD because I see serious flaws in the conventional understanding of TDD.

Much of the current view of TDD includes:

  • Units are tested in isolation of the rest of the system.
  • Unit tests are more important than any other form of testing.
  • A “unit” is a class or a function. Nothing larger is a “unit”.
  • If you test more than one class, that’s an integration test.
  • If you test from the API with all resources, that’s a system test. Let QA deal with that later. Isn’t that exactly where waterfall failed?
  • You can’t write any production code without a failing test.
  • You have to write only one test at a time, and it must fail.
  • Tests have to be fast.
  • Therefore, they cannot touch hardware, the file system, other services, or database.
  • Tests should be short.

All of this rubs me the wrong way.
I’ll get to my thoughts later, but my concern about this cemented view of TDD caused me to be very interested in the current talks.

On to the discussion

I came in after the 2nd video, while doing research on Agile and TDD.
I’m not sure if the order matters, but here’s a list of what I know about the discussions.

DHH presentation and posts

David Heinemeier Hansson gave the opening keynote at RailsConf 2014 on April 22.
Fast forward to like minute 30 if you want to jump to the part where he starts talking about TDD. But the rest of the talk is great too.

Some views from the talk:

  • Lots of developers that push TDD make you feel like your code is dirty if you are not using TDD.
  • Driving your design from unit tests is not a good idea.
  • The TDD notion of “tests must be fast” is shortsighted.
  • The faith in TDD can lead to completely forgetting about system testing.
  • The focus on the unit and the unit only doesn’t help with producing a great system.
  • 100% coverage is silly
  • Programmers want software to be a science, but it isn’t. It’s more like creative writing.
  • Good software isn’t like engineering.
  • It’s like writing. Clear concise writing is better than convoluted writing.
  • Clarity is good. So good that clarity should be the number one goal, not test coverage or test speed.
  • Being a good developer is as hard as becoming a good writer.
  • Just like writing, the way to become a good programmer is to write a lot of software, read a lot of software, aim for clarity.

Then he wrote a handful of posts:

These are all interesting reads. Highly recommended.
BTW, I’m not a Ruby or a Rails developer.
His comments apply to any language.

What do Martin Fowler and Kent Beck think of all this?

Kent Beck

Kent Beck has been the main proponent of TDD from the very beginning, as part of Extreme Programming. I’ve read quite a bit from him. So, yes, I’m quite interested in his viewpoint on all of this.

Even though a lot of the extreme views of TDD start with Kent’s writings, I don’t think the dogma is coming from him.

Quote attributed to Kent from DHH talk:

“I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence” – Kent Beck

Awesome!

Martin Fowler

Martin Fowler has written lots of great posts on software in general, and agile methods.
(A great overview of agile is written by Martin: The New Methodology)
I believe it was Martin’s initial contacting DHH over one of the posts that started this conversation.

The discussion, “Is TDD Dead?”

These three are talking about this with a series of public discussions.

Here are the links for the videos so far. (I’ll try to keep this list updated as they come out.)

Is TDD Dead? recorded Google hangouts:
Part 1, Part 2, Part 3, Part 4, Part 5 & 6

Martin also has a summary of the talks here.

My opinions

I love TDD.

But my version of Test Driven Development is probably quite different than yours.

Ever since I read about Test First Programming and TDD in the early days of Extreme Programming (XP), I grabbed on to it like a shipwreck survivor holding on to a flotation device.

When taken as just a darn good idea, and a goal, TDD is superb.

When taken as the only true professional way to do things, it’s just as annoying as waterfall.

TDD often has a misguided focus on units over systems

I think TDD is most useful when used at the system level.

The system is where your users interact with the software, so that’s where your tests are most important.

Focus on what customer need your new functionality is fulfilling.
The first tests should be if your feature actually satisfies the need, as a system.

Functional system tests test from the users perspective.
Isn’t that most important.

I want my code to be agile. To be adaptable. To be refactor friendly.
It’s the user level system tests that I really don’t want to break.

A redesign of part of a system may break many unit tests.
But it better not break any functional system tests.

So isn’t the system level the place where we really want most of our testing effort to go?

Functional system AND unit

Both functional system tests and unit tests are important.
I think functional is more important because that’s what the customer cares about.

Unit tests are great too.
Especially with tricky parts of the system.

I heard recently a great rule of thumb. If you feel like some class or method needs an explanation with a block comment, this bit of code needs a unit test.

Also, unit test the parts that are likely to fail.

The parts you don’t understand a lot.

The parts that keep getting questions about.

Slow tests are fine.

I’ve got 24 hours in a day and a continuous integration / continuous test server running for all 24 of those hours.
Even if all of my tests take 2 hours to run, my entire suite is going to be run 12 times per day.

Are you really checking in your code more frequently then that?

Isn’t that good enough?

Mocking just for isolation is bizarre.

There are reasonable times to mock stuff.

The best reasons I’ve come up with are:

  • Simulate infrequent events such as failures, error codes, etc.
  • Simulate resources that are not ready yet.

I don’t mock to isolate just for testing sake.
I just don’t think that way.

TDD is not dead

But let’s kill the dogma, the snobbery, the elitism.

Software IS quite like writing.

Don’t let your first draft be too precious.

Costly to fix small perfectionism

This is a subtle point.

Parts of your system might be over engineered.
Software engineers are very likely to violate YAGNI and make a class or a subsystem do more is needed for the current release.

Unit tests around classes and subsystems will test more functionality than is necessary. They may also be more rigorous than the real requirements of the system.

What if a unit test fails?
You should fix the code.

What if the failure is NOT reproducible at a system level?
Meaning I can’t write a system test that causes the failure?

Well, then the unit test is either more strict than the system requirements for the unit, or the unit test is testing a feature not used by the system at all.

Then, should you fix the code?

How much time and money are worth the effort to fix it?

Would you delay shipping?

When you have blind faith in TDD, I think you would spend a lot and delay.

I think that’s wrong.

If you like this…

Get notified of new posts, delivered to your inbox. No spam. Not very frequent.

Comments

  1. says

    I agree with most of what is said here. I like the principles, but not the dogma. However, this part got my attention:

    > Even if all of my tests take 2 hours to run, my entire suite is going to be run 12 times per day.

    Don’t get me wrong, I don’t necessarily buy the fast unit test stuff. Sometimes, due to a huge number of tests or to using databases, filesystem and stuff like that, you’ll have “slow” tests. However, if your tests take 2 hours to run, the code that you’re checking in right now will be only be on production in two hours (assuming you have a continuous delivery pipeline set up). While this delay is not a problem for some, it is for others. And, for those that timing is essential, it is highly desirable that minor corrections go to production as fast as possible. How you’ll achieve this is up to you, from changing your tests to parallelizing them, but I think it would be nice to point out this timing stuff.

    • says

      I did mean the “2 hours to run” to be:
      1. A hypothetical time that still should be now problem.
      2. The time for the full integrated system functional tests.

      With embedded systems, test equipment, signal settling times, FPGA load times, measurement averaging, DUT handshaking, etc. a full system test that takes somewhere between 30 min to 2 hours is really not out of the question.

      Of course, with these kinds of times, the normal TDD model needs to be modified.

      For folks dealing with long test suite times, I usually recommend breaking the suite up into subsystem tests, and then further into smoke tests that can be run quickly on the developer bench before checkin.

      TDD still works great, even with issues of long times.

      That’s my point.

      Even if it takes a couple hours to find out you broke something you didn’t intend to, that’s way better than tossing it over the wall to some QA team that may or may not find the problem some days in the future.

  2. says

    > Even if all of my tests take 2 hours to run, my entire suite is going to be run 12 times per day.

    Yeah, it also means that if you check in broken code, you’ll find out two hours later. While you’re busy talking to a customer. Or working on something else.
    If you’re using a dynamic language like, say, python, you’re finding out about a type error two hours too late.

    > There are reasonable times to mock stuff.
    >
    > The best reasons I’ve come up with are:

    > Simulate infrequent events such as failures, error codes, etc.
    > Simulate resources that are not ready yet.

    What about clarity? A one-liner mock that tells me that I read from a file with an “ERROR” in the first line is better than wading through the filesystem, searching through the test file that is loaded, isn’t it?

    > I heard recently a great rule of thumb. If you feel like some class or method needs
    > an explanation with a block comment, this bit of code needs a unit test.

    That’s a really great rule.

    • says

      Yes. Immediate feedback is better than 2 hours later.
      But some systems just take that long to test. And I don’t think you should give up hope just because of long test times.

      Regarding clarity and file errors, etc. I intended to include that in “simulate events such as failures, error codes, etc’”. Your example is a good one. I agree.

Leave a Reply