Archive for Testing

The Curse of the Singleton

It took us six weeks to break the curse of the singleton. Six weeks! By the end of it, we’d rewritten most of our data access layer.

We began the process of removing singletons innocently enough. I thought I was well prepared for the task. I’d just finished reading The Pragmatic Programmer (my review of The Pragmatic Programmer) and Working Effectively with Legacy Code (my review of Legacy Code). I remember telling Gordon I’d tackle the problem over the weekend…

What’s a singleton?

The Singleton Design Pattern is one of the first patterns introduced in many software design books. But don’t let this fool you like it did me. Its prominent position has nothing to do with its importance. The Singleton is usually listed first because it’s the easiest pattern to explain and implement. It made a convenient place for the author to start, but the Singleton’s real uses are very limited.

Which is appropriate, actually, since the real use of the singleton is to limit usage. A class that implements the Singleton pattern allows only one object to be instantiated at a time. There are a few cases where this is desirable. For example, classes that control access to a single hardware device or that set up global variables. But the danger of the Singleton is that there are many cases where you’ll want to misuse it.

Why are they bad?

Scott Densmore lists the four key characteristics of the Singleton and how each can get you into trouble in his Why Singletons are Evil blog post.

For another cautionary tale of the cycle attraction, infatuation, disappointment, and rejection, read Singleton, I love you, but you’re bringing me down.

In our case, we’d gleefully implemented Singletons for database access, content indexing, security and access control, and in a few other places where we thought we needed just one instance. If Steve Yegge were here, he’d call what we’d done an instance of the Simpleton pattern — a failure to grasp basic principles of object-oriented programming. You can read more about Yegge’s thoughts on the singleton and design patterns for dummies.

Our automated tests were running slowly because we had to set up and tear down the database for every test. Making a change to one component would frequently cause several tests to fail. Everything was tied together at the hip — at the Singleton classes — and it was impossible to disentangle our code to test particular items in isolation. We had tests, but not unit tests. They were integration tests, and the points of integration were the handful of singleton classes we’d built.

Worse, our database performance was lousy. Since we had a global variable for our database object, we could sprinkle database access code throughout the rest of our object model. We discovered that we were opening and closing database connections all the time. And we’d had to implement tricky locking code to guarantee that our SQL statements would get executed in the right order.

What did we do about them?

The Singleton let us be lazy about our programming habits. It allowed us to make assumptions we shouldn’t have. You can call it premature optimization or a retreat into procedural programming techniques from an earlier era. Ultimately, we’d found that it allowed us to cut too many corners.

So we slowly rooted out each Singleton class from our API and reimplemented the functionality in other ways. Fortunately, we had a large battery of integration tests to help guide us. And luckily, we’d decided to tackle the problem during our first Alpha test, when we could still afford to make sweeping changes. But correcting bad design takes much longer than avoiding it in the first place — even if you’ve read all the right books.

Six weeks later, we finally sorted out the mess we’d made for ourselves. There’s a handful of odds and ends left to do, but the design feels better. My gut tells me it’s an improvement, and our tests — now we have both unit and integration tests — show that we’ve almost tripled the speed of the data access layer.

It was worth our time to break the Curse of the Singleton. Beware lest ye, too, fall under its spell!

Review: Working Effectively with Legacy Code

I know what you’re thinking: “Infovark’s been around for barely a year! Surely you guys aren’t having to deal with legacy code already?” If you accept Michael Feather’s expansive definition of legacy code — code without unit tests — then yes, despite our best efforts, we have lots of legacy code.

But even if you don’t buy that definition, and even if you’re working on a completely greenfield application, chances are you’ll have a lot of code in your project that isn’t fully understood. Or perhaps isn’t fully understood by all members of the team. And it’s in dealing with this issue that Working Effectively with Legacy Code really shines.

What happens when you need to change code that isn’t fully understood? Are you making it better or worse? The author says that you can’t know the answer to this question without tests in place. Having documentation is nice, but unit testing provides measurable output.

The brass tacks

Unlike many programming books, this one is organized in a Q&A format. Once past the introduction — which you can skip if you already understand the importance of refactoring and test-driven development — you’ll find the chapters organized by topic. Here’s a sample of a few chapter headings:

  • It Takes Forever to Make a Change
  • I Can’t Run This Method in a Test Harness
  • Dependencies on Libraries are Killing Me
  • I Don’t Understand the Code Well Enough to Change It
  • This Class is Too Big and I Don’t Want It to Get Any Bigger

This is a great way to organize a highly technical book. Each chapter has a specific purpose. The author then spends the chapter discussing the ways you can get out of the jam and weighing the pros and cons of each.

You’ll find in-depth examples of each of the techniques used, but be prepared to shift between languages. To get the most out of the book, you’ll need to be comfortable scanning unfamiliar syntax.

As a bonus, the book contains an index of common refactoring patterns. Certain patterns make appearances in more than one chapter, and the index provides another place for the author to work through some real-world examples.

All in all, this is a practical field manual for a set of problems that occur too often out in the wild. I highly recommend it.

Swapping NUnit for xUnit?

In our previous programming jobs, neither Gordon nor I had to do much testing. We tended to focus on architecture and development issues. Just prior to forming Infovark, we were acting as consultants for an ECM vendor that has since been snapped up by HP. So while we were aware of things like unit testing and test-driven development, we’d never had to do much of it ourselves.

We knew it was important, though. We’d seen too many things go wrong in software projects that didn’t make testing a core part of their standard operating procedures. So we incorporated testing into our product development and we’ve been learning about this testing stuff as we go. (It always happens that way, doesn’t it?)

We began with NUnit, the gold standard for unit testing on the .NET Platform, and quickly discovered some things that everyone else already knew:

  1. Good unit tests are essential for refactoring. It’s the only way you can make big OO changes with confidence that you haven’t broken anything that used to work.
  2. Good unit tests are are essential for troubleshooting. It makes it much easier to pinpoint the source of the errors when things go wrong.
  3. Good unit tests are essential for making your product fail gracefully. Every program will fail at some point. It’s how a program reacts to a failure condition that counts.
  4. You can’t write good test suites without good specifications. You’ll forget to test key assumptions and dependencies. You won’t confirm error conditions. You’ll couple your tests to your current object model, rather than to program requirements.
  5. If you’re doing it right, you’ll write far more code for testing than for the product itself. It’s much harder to verify correctness than it is to write correct code.
  6. Unit testing is different from integration testing. Integration testing is different from system testing. All three are important.

In addition to these common nuggets of TDD wisdom, we also discovered something else: It’s dreadfully easy to shoot yourself in the foot with NUnit tests. It’s easy to write tests that aren’t atomic, for example, or tests that expect errors to occur in one line of code that pass because another line of code throws a similar error.

To address some of these quibbles, a splinter group has launched xUnit on CodePlex. It aims to simplify testing syntax, incorporate more .NET 3.5 constructs, and prevent some of the more common NUnit anti-patterns that testing neophytes (like us) regularly develop.

We’ve messed around with XUnit a bit, and so far we like what we see. It’s not enough yet to make us abandon NUnit though, especially with NUnit 2.5 on the horizon. Besides, the NUnit framework is integrated with a variety of other development tools we’ve found indispensable. (I.e., everything made by JetBrains.)

But that’s just our opinion. What do you think? How do these things stack up? And what about the testing framework built in to Visual Studio Team System? We’d love to hear your experiences with these tools.