Making your code (more) testable.

It is a well-established concept that the best way to get 100% code coverage is to write your unit-tests from the very beginning of a project, such that each test influences the code, and the code immediately influences the tests as it evolves. This is more than just a strictly dogmatic approach to code construction reserved for ultra-ninjas and people who hire dominatrices (I’ve never had to pluralize that word before, I swear) to dig their heels into their backs for pleasure, this is good advice. Test-driven development is the best way to get solid code coverage, but for reasons often beyond our control we don’t have the luxury of TDD. Either because we’ve inherited a brownfield project with lots of legacy code, or because our deadlines are tight and we just don’t have time to convince our pointy-haired bosses that having tests provide business value. Or we’re just lazy.

This puts us in a bit of a bind. As good developers we know that having good code coverage is a good thing. It’s like flossing, or eating our vegetables; we know we should do it, we know it’s good for us and will prevent a whole host of misfortunes later on, but we don’t because we think it’s a chore. We might write a few tests later, when we have time, or when we need to debug something, but by then the code has morphed into untestable spaghetti. All hope of an isolated repeatable unit-test has gone out the window, and the best we can hope for is something that requires a lot of setup, and database, and possibly some services, and a bit of luck to work once, followed by more code to tear-down all the changes the test has made. Sound familiar?

So, you’re not into TDD, but you don’t want writing tests to feel like agony. Luckily, there is a middle ground. Write your code as if you plan to test it later. Design your code in such a way that testing it should be easy. If you follow a few fundamental rules you will find that in addition to being protected from regression bugs when you write your tests, your code will get measurably better and contain fewer bugs simply by virtue of the WAY you are constructing it.

1) Limit side-effects.

Okay, if you’ve ever listened to any academics talk about functional programming you are probably sick to death of hearing about immutability and side-effect free code, and I’m not advocating exactly that, but simply asking you to think about how your code will be used with respect to state. Does your code behave the same way the first time as it does the third? This concept touches on a function’s purity, in the mathematical sense. Put simply, if a function is pure then testing it is easy. If a function uses lots of state and is highly coupled with other classes, then testing becomes that dreaded chore we are hoping to avoid. Set yourself up for success and keep your classes and methods small, uniquely responsible for its’ functionality, and have fewer dependencies (both in terms of state and additional classes it needs to accomplish its’ job), and you may find that your code is not only more testable, but also easier to understand and more resilient to change. Isn’t that nice?

2) Depend on abstractions.

Your eyes just rolled back thinking about the D in S.O.L.I.D. right? Yeah, well, I’m not sorry. It’s good advice if you apply it correctly. I’ll use the canonical example of LINQ which I assume everyone in the .Net world has used by now (and should probably stop abusing). The power and flexibility of those query methods is directly related to a single elegant abstraction ( IEnumerable<T> ). Each method in that stack only needs to know how to enumerate some collection to be effective. These methods work on linked-lists, arrays, sets, even some crazy infinite “virtual collections” that materialize data only when the next item is requested.

In the same way, your own code can be flexible with respect to the arguments used to call it. Generalize! Does your method really require a concrete instance of Collections.Generic.List<T>? Are you adding and removing elements? If so, maybe try IList. Need just the Count property? Downgrade your required parameters to take an ICollection. It doesn’t even need that!? Your method just uses foreach over the collection of things and does something for each thing. Have I mentioned how useful IEnumerable is?

Your code is now much more useful, and also provides some added context about how it will use the parameter to the callers. IEnumerable gives them a nice guarantee that you (probably) won’t do something sneaky like adding or removing items from their collection. Obviously if you were malicious you could try casting it to some other interface that supported that behavior, and then tamper with the collection, but those are the sorts of ill-willed malcontents who would cast off their const-iness in C++ and we should just try to avoid them and hope for the best.

Stay tuned until next week when we talk more about writing testable code with some practical examples. Same smug time, same smug channel.

7 thoughts on “Making your code (more) testable.

  1. That is really interesting, You are an overly professional blogger.
    I have joined your rss feed and sit up for in search of extra of your fantastic post.

    Also, I’ve shared your site in my social networks

  2. Currently it appears like Movable Type is the preferred blogging platform out there right now.
    (from what I’ve read) Is that what you are using on your
    blog?

  3. What i do not realize is in reality how you’re no longer really much more neatly-favored than you might be now.
    You are so intelligent. You recognize thus considerably
    on the subject of this subject, produced me individually imagine it from numerous various angles.
    Its like men and women aren’t fascinated until it’s one thing to accomplish with
    Lady gaga! Your own stuffs great. All the time deal with it up!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s