Skip to main content

The Unreasonable Effectiveness of Testing

When Albert Einstein was formulating his theory of General Relativity, he hit an annoying snag. His first attempt at the equations didn't seem to work properly - they only worked if the universe was curved, whereas everyone knew that the universe was flat. So, he added a fudge factor called the cosmological constant, which seemed to fix everything.

However, subsequent work (principally by Edwin Hubble) showed that the universe was in fact curved.

It's almost as if the equations were trying to tell Einstein that the universe was curved. This is a well-known phenomenon - Eugene Wigner described it as "The Unreasonable Effectiveness of Mathematics".

A similar phenomenon occurs with testing.

We all know that testing is reasonably effective. It's a very effective way to identify bugs in your code, as it should be.

But I also think it's unreasonably effective. For example, testing improves the quality of your code even if you never actually run your tests.

Wait, what?

Think about what you need to test your code. You need to be able to test its components in isolation, or test a subset of its components. To do this, you need to be able to assemble the components of your system in different configurations, with components missing, stubbed, or mocked out. If you want to stub components out, they need simple interfaces that can be readily faked/stubbed/mocked. You need multiple entry points, to allow you to connect test harnesses and other test fixtures.

Look carefully at these requirements. They're also the requirements for re-usable, modular code. At some point in the future, you'll need to re-use your code for something you never thought of. When that happens, you'll thank your lucky stars that your system's composed of small, interchangeable components, that have simple interfaces, that can be recombined in different ways, and aren't bound too tightly to each other, or your current configuration.

Similarly, at some point in the future, a nasty bug will show up in your code. When that happens, you'll be glad that the bug is confined to a small, easily replaceable component, and not woven into the fabric of your application.

I've been there. I was working on a project that had a front-end and a back-end. I wanted to be able to test the front-end in isolation, so I added the ability to inject a stub back-end. A few weeks later, a requirement surfaced which required the back-end to be configurable. So, a piece of code that was originally added to facilitate testing was pressed into service for a production requirement.

It's almost as if the tests were trying to tell me I'd need the back-end to be configurable.

This is a slight exaggeration, but it's an interesting convergence of ideas. The things you need for code to be testable are roughly the same things you need for your code to be re-usable and maintainable. If you make your code testable, you get these other things almost for free.

So testing starts making your code better, even before you run the first test.

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.