Can We Give the Unit Testing is Dead Hot-Takes a Rest, Already?

by Hit Subscribe 21. February 2019 06:43

You've heard it before. Unit testing is dead. Test-driven development (TDD) has failed. Tests are a waste of time.

Can we give it a rest?

Unit testing isn't dead. TDD hasn't failed, not even if you can't get it to work for you. And if you're not testing your code, can you paint a big red 'X' or something on it so I have a warning if I come across it?

no more please for unit testing hot takes

TDD is Extremist

One argument against TDD is that its advocates are mean-spirited fundamentalists. TDD is "an unrealistic, ineffective morality campaign for self-loathing and shaming." This argument presents TDD as an all-or-nothing choice and it often morphs into a reason to give up on all unit testing.

In regards to the first part, TDD does have some strong advocates. But this isn't a reason to ignore or abandon it, though. Take a step back from social media and ignore the extremists. TDD is a tool that can be used to help write better code. If someone tells you that you're doing it wrong, take the feedback for what it's worth.

But even if you decide that you don't want to use TDD, that doesn't mean you should stop writing unit tests. It's not all-or-nothing. Automated testing is a valuable tool, and writing tests for your code after the fact is an essential activity.

Integration Tests are Better Than Unit Tests

No, integration tests are as necessary as unit tests, and end-to-end tests are vital too.

Testing helps you manage change. Unit tests find breaking changes inside a program (or library). Integration tests flag breaking differences between programs. You've heard the old expression about comparing apples and oranges, well this is like comparing apples and apple pie.

Waiting to catch problems until you deploy your code is inefficient. You can detect many problems at compile time with effective automated tests. Also, problems are easier to find and fix when an isolated test discovers them—finding a bug with an integrated test is often much more difficult than with a unit test.

Unit tests aren't a panacea. There's an entire class of problems that they can't find. As I said, they're different. But they can save you a lot of trouble before you deploy your code.

Unit Tests are Assertions in Disguise

People often cite Coplien's Why Most Unit Testing is Waste as a defense for doing fewer unit tests, or just not doing any at all. It has many good ideas, and it makes a compelling case against poorly written unit tests. But it doesn't make a case for giving up on unit tests completely. Even the title has "most unit tests" in it, not "all unit tests." And we'll expand on this more by breaking down this quote:

When I look at most unit tests — especially those written with JUnit — they are assertions in disguise. When I write a great piece of software I sprinkle it with assertions that describe promises that I expect the callers of my functions to live up to, as well as promises that function makes to its clients. - Coplien

Yes, unit tests have assertions in them! They do exactly what you need a test to do—fail when a condition isn't met. Test assertions aren't like the ones that belong in production code. They use the same mechanism, but not for the same reason.

In tests, assertions are for checking the results of an operation. Each test verifies the behavior of a distinct piece of code under specific circumstances. If you need to test code under several different conditions, that means several tests. The assertions check the result and stop the test if it fails.

Shipping code with assertions turned on is a controversial subject. But it shouldn't be. Dead code tells no tales, after all. The best reason I can think of to not use assertions in production is that exceptions often work better. Assertions are exceptional errors, which is why an exception might be a better option.

The point is, assertions in tests are a way to verify the result of an operation and stop the test if it fails. If you're "turning your unit tests into assertions," they're inappropriate tests, or you're filling your code with error checks that belong somewhere else.

Tests Need to be Maintained

Let's present a scenario:

Imagine you have three components, A, B and C. You have written extensive unit test suite to test them.

Later on you decide to refactor the architecture so that functionality of B will be split among A and C. you now have two new components with diferent interfaces.

All the unit tests are suddenly rendered useless. Some test code may be reused but all in all the entire test suite has to be rewritten. - Unit Tests Fetish

If you refactor your code, you need to rewrite your tests. That's the point.

Some developers see tests as part of the job. Their code isn't complete unless tests accompany it. For others, tests are optional. They're "nice to have."

I wouldn't dream of pushing a major refactor of working code to QA, let alone production, without tests. New tests are part of the refactoring. That's the job.

But some folks disagree. They might think like this:

I have seen a nice definition of "architecture" somewhere. It said that architecture is that what makes software able to change. If we embrace this point of view, unit tests can be considered to be a strictly an anti-architecture device. - Unit Tests Fetish

That follows the same logic as saying, "That's why I stopped eating. I know I'm just going to get hungry again."

Throw Tests Away

Let's go back to Coplien for a moment. One of his recommendations is to "Throw away tests that haven’t failed in a year." He goes on to explain this reasoning when he adds,

Most programmers want to "hear" the "information" that their program component works. So when they wrote their first function for this project three years ago they wrote a unit test for it. The test has never failed. The question is: How much information is in that test?

I've written tests like this, and if you're a unit-tester, you have too. But is this the main reason tests exist?

Let's take a look at a quote from Kent Beck, the man who literally wrote the book on TDD:

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 (I suspect this level of confidence is high compared to industry standards, but that could just be hubris).

For Beck, tests are about confidence. The confidence begins when you're writing the original application. But it doesn't end when the code ships. It needs to stick around after you make some changes. You'll want to lean on it when you refactor it, even if it's 13 months later. And the developer that comes after you will appreciate the confidence she feels when she makes a change and starts a build.

Unit Testing Lives

Unit testing isn't dead. Integration and end-to-end testing haven't replaced unit tests. There was never a point when integration tests weren't necessary because unit testing satisfied our testing needs. So why would we stop one because we have the other?

Unit tests are more work. They need to be written and maintained, just like application code. They're part of the code, and the fact that they need to grow (and maybe shrink) with it is a feature, not a bug.

So reports of unit testing's death were an exaggeration. It's here to stay.

This post was written by Eric Goebelbecker. Eric has worked in the financial markets in New York City for 25 years, developing infrastructure for market data and financial information exchange (FIX) protocol networks. He loves to talk about what makes teams effective (or not so effective!)

Tags:

Blog

Month List