This is a practical hands-on guide for developers new to the concept of unit testing. I decided to write it after talking with many developers who have read a whole lot of material on the subject, but still had no idea about how to actually write unit tests and implement them in their development process. This article then, is aimed for the developer who is still very new to unit testing (so don’t expect any advanced topics or tips).
First, read about unit testing, why it’s good, and how it can be put to practical use. After you’ve worked through this tutorial, be sure to read up on some counter-points to unit testing. It’s worth knowing everything you can about what you’re getting yourself into. Some people believe unit testing code makes for stronger applications while others believe it’s a waste of time.
IMO, I find it useful as a process tool. You’ll read a little more about how I use unit testing later in this article. I suggest you keep an open mind and draw your own conclusions. So if you’re ready, let’s get going shall we?
The Most Basic Concept
The only critical concept to understand at the end of the day here, is the unit test itself. It’s a humble concept and when you get it, it’s a quiet little, “Oh – that is simple.” And when you start using the little guys in bigger and bigger projects, you’ll thank yourself for taking the time to become friends with the helpful little unit test.
So to start — a good unit test is born from a tiny story. Some people call these stories use cases. They’re only a few sentences long at most and describe a single interaction a user will have with your program. If it doesn’t fit on a 3x5 card, it’s too long of a story. A typical story goes something like this: “The user clicks on the history tab to display a list of past transactions in the main window.” We then use this story to birth our unit tests.
How a Unit Test is Born
We create a unit test first by breaking down the story into its steps. In our example, the first step would be the user clicking on the tab. The second step would be fetching the data to fill in the list and the final step would be displaying the list in the main window. Very small steps for such a simple action, don’t you think? That’s why we call them unit tests — they test the smallest units of our program.
The most important part of creating unit tests is to have patience and not be hasty. We’re not writing application code yet. Instead, we’re going to write the unit test first. The reason we do this is so that when we write code, we can verify that the output of our efforts is in line with what we expected before we wrote it. It’s the difference between having a recipe and making it up as you go: when you write unit tests, you know what the end result should be before you go to the kitchen.
The beauty of this process is that you’ll know if something is not right early on. When you make things up as you go, you run into problems after you’ve done all the work. It’s that final taste test at the end to see if it all paid off. Sometimes you get something good, and it’s certainly fun to be free and creative; but when a client’s budget is on the line or your limited time and money… maybe being a little more thoughtful and conservative will save you from scrambling back into the kitchen at the last minute when your ad-hoc recipe didn’t quite work out.
So once we’ve written a test, it’s all gravy. We go to the kitchen, open up our editors, and write the code to pass the test. When we’re all done, we run the tests and hopefully they all pass. When they do, we move on confident that everything is working.
(The advanced reader will know that sometimes this isn’t the case, but that’s what functional or integration testing is for which is a topic for another day).
The Code
So enough talk — what does the actual code look like?
Well, I’m going to use Python to demonstrate unit testing today. As part of my demonstration, I’m going to be using something called, Nose. It’s called a “unit testing framework;” which is just a fancy system for automating your testing. It makes life really, really easy as I’m about to show you. Before we begin, just go ahead and install Nose on your system.
First – lets just set up our working directory. This is akin to preparing your kitchen for the feast you’re about to create.
../my-project
/tests
test_arithmetic.py
arithmetic.py
One reason for using something like Nose is so that we can keep our test code separate from our application code. It’s just good house cleaning. That way as you get ready for deploying your application, you just have to make sure you don’t package up your /tests folder with everything else.
The next part is to write our test class. Notice the naming convention — it’s important to stick to it as Nose uses it to find your tests. Check out the Nose tutorials if you need more information. Read on for how to write the actual code!
from arithmetic import Arithmetic
class TestArithmetic:
def test_add(self, a, b):
a = Arithmetic()
result = a.add(2, 2)
assert result == 4
Here’s the breakdown:
That’s right folks, for once in our lives it’s encouraged to make assumptions!
Now, we go write the code to pass that test. In this example, it’s simple enough that we can just write the code. However, in practice you will probably be testing slightly more complex units. This is okay — what you would do in that scenario after writing your tests is set up the method without any logic in it. Then you run the tests which will of course fail. What this does is lets you catch errors in the test itself. There’s no greater evil than passing a test that is flawed to begin with!
So in this tutorial, I’m just going to go ahead and spare you a step. In the next example, I’ve set up the class, method, and have written the logic to pass the test in one step. Just keep in mind that in practical application, you may repeat these steps a few times. You may write the test, run it and fail (or find that you’re making the wrong assertions, in which case you’d correct the test), write code, run the tests… and so on until all your tests pass.
class Arithmetic:
def add(self, a, b):
return a + b
That’s it.
So now you’re all set up and ready to run your tests. Make sure you’re in your project directory and run the command “nosetests”. Nose will now search through your project files for tests and run them for you. If all went all, you should see output that looks like the following example:
..
----------------------------------------------------------------------
Ran 1 tests in 0.004s
OK
That means your code has passed the tests and everything works as you expected it to. Good job! Give yourself a pat on the shoulder; you’ve just taken the first step into a new world of productivity and output quality.
As you learn more about unit testing, you’ll be introduced into concepts not covered here. Don’t be discouraged — it always comes down to this, no matter how complicated it sounds. Just keep pushing ahead; you’ll thank yourself later when you see how much this approach to development reduces the bugs in your code and the amount of time it will save you in the long run.