Lost in LoC


Introduction to Unit Testing

Posted in Development by Ryan Baldwin on May 11, 2008
Tags: , , , ,

The last couple months I’ve been doing a lot of unit testing at work. A lot. And I’ve been doing it more consistently than I have in the past. The result has been dramatic, not only in the quality of the code that I’m producing, but also in the confidence that I have when making changes to existing code. Even more importantly, however, is the impact that this dedication has had on my knowledge base, as I’ve had to do a lot of reading out of the necessity of creating maintainable, sustainable test suites. This article (and likely a few articles to come) is a breakdown of what I’ve learned so far. If you’ve learned something (or if you know something and you see me learning the wrong things) then I request that you, by all means, please share your knowledge with me and the rest of the world!

What Is a Unit Test?

In a nutshell, a unit test is a chunk of code that tests a small, specific chunk of production code (the System Under Test, or SUT). The Unit Test can assert that the SUT does something, does not do something, returns data (or no data), or exhibits some specific behaviour. Essentially, the Unit Test asserts that the SUT does what it’s supposed to do consistently and predictably.

There’s a subtle but important point in the above that I should emphasize: the SUT is a small, specific chunk of production code. People sometimes want to test bigger chunks of code, or multiple pieces of code (such as testing how a piece of data progresses from Point A through Point B and finally out of Point C). There’s nothing wrong with these types of tests, and indeed these tests (called Integration Tests) should, by all means, be written. But do not confuse them with unit tests, as unit tests are much, much smaller in scope.

Why is Unit Testing Beneficial?

There’s a number of benefits to unit testing, but the majority of them are not immediately noticeable. First and foremost, unit testing can help you detect and avoid defects in your code not only before you ship your code, but before you commit your code to whatever code repository you’re using (you are using source control, right?). On the outset this might not seem like a big deal if you’re writing a small, simple class; however if/when that class gets grows and grows and then gets refactored, those unit tests will be a God send by telling you what you just accidentally broke in the system. From a regression testing and quality standpoint Unit Tests are enormously beneficial.

Well written unit tests (and unit test suites) also act as documentation which illustrates what the SUT is and is not supposed to do, and also how the SUT is used. In essence, the tests are the specifications of the SUT. They act as examples, and these examples are significantly important to the people that will inherit your code after you’ve left your current employer (which, be honest, is inevitable). In fact, they’ll even help you when you return to that class 8 months after you wrote it because somebody in marketing wants your Duck class to be able to play the banjo. Having the ability to review your code, how it works, how a programmer should interact with it etc. all from a single location, without having to do a Find All across tens, hundreds or thousands of code files is pretty convenient. Enjoy The Convenience Of Knowing Where To Look! Yay Lazyness!

There are about a zillion other benefits, but I’ll only mention one more because it ties into a bigger concept of unit testing; under certain conditions unit tests can help you design better, leaner code; the practice of what’s called Test Driven Development. The idea behind TDD is that you define, via tests, what the SUT needs to do, and then you write the least amount of code possible in the SUT to pass the tests.

What Are Some Unit Testing Cons

Not all that glitters is gold, and while the benefits and advantages of unit testing are enormous, they do come at a price.

For starters, unit tests aren’t free. At the end of the day, a Unit Test is software, and that software has to get written by a developer, and developers cost money. If you’re going to use TDD or implement unit tests at a later date you will have to take that into account when making estimates. If your biggest goal is to hammer out as much code as possible in the absolute shortest amount of time possible, quality and scalability be damned!, than unit testing might not be for you. However, if you’re working on a long term project (either for yourself, your company, or somebody else’s company), the cost of adding on some time for a higher quality product is a no brainer (hint: do it).

Another con is one you’ll notice pretty quick: unit test organization (and unit tests themselves) can get complex. Just like production code is difficult to write in a sustainable and maintainable way, test code can be even more difficult. The principles and strategies that exist in writing “real world” code are still applicable to test code. You will experience the same heart-ache, frustration, and demoralization of poorly structured tests as you do with poorly structured production code.

Many people don’t take this into account when writing unit tests, and when things start to go awry and the various smells of unit tests start to rear their ugly head, it’s easy to just say “forget it” and drop unit testing because “it isn’t worth it.” But these test smells, much like code smells, can be avoided (or at least minimized) by learning from the mistakes of others. The book xUnit Design Patterns – Refactoring Test Code is the holy bible of unit testing. If you’re a beginner or experienced TDDer, this book should be on your shelf. However, if you’re like me, you’ll find the book is right next to your keyboard – there’s no sense putting it back on the shelf! I consult it daily!

Don’t treat unit tests as a second class citizen; just because they aren’t getting shipped doesn’t mean they take a back seat to basic OOPL principles. Be kind and thoughtful to your unit tests, and they will repay you like, ten fold, dude.

Just do Them!

Just like everyday production code, there can be many gotchas to unit testing; but they’re nothing a bit of reading, research, and careful design can’t take care of. The long term benefits of unit testing are enormous. The project that I’m currently working on has only been under development for 2 months, and we’re still learning the discipline of unit testing, but I already have 300 tests (77 of them are on a small, single component of the code). That’s 300 tests that will not only let me know when something no longer works, but if my tests are structured well, will tell me exactly where the something went wrong.

This article was a (very) brief introduction to Unit Testing. In my next article I’ll outline for you the different components of a unit testing framework, and hopefully from there we can start building some examples.

Stay tuned!

Do you have experience with TDD (or Unit Tests in general)? Share your knowledge and experience in the comments!