The idea of unit tests has been around a long time, and most people agree that writing unit tests is a good idea. However, when deadlines loom and time is at a premium, the unit tests often don’t get written. That’s a problem, because studies have shown that projects with good unit tests often are more robust, with fewer bugs, than projects that don’t have good unit tests.
In a traditional development process, when a programmer needs to create a new function, he writes the function, and then, if he has time, writes a unit test for it. If he doesn’t have time, he doesn’t write the unit test: he tests the function in the context of the program being developed. One day, someone decided that it might be a good idea to reverse the order: write the unit test first, and then write the function. That led to the idea of Test-First Development.
In this section, we’ll explore the idea of test-first development to see how it can help.
A programmer using Test-First Development writes a new function using the following steps:
First, create the function interface and docstring.
Next, create a unit test for the function.
Run the unit test. It should fail.
Write the body of the function.
Run the unit test. If it fails, debug the function, and run the test again. Repeat until the test passes.
As an example, suppose that we’re going to write our
sumnums function using the Test-First methodology. We begin by creating the interface and docstring:
def sumnums(lo, hi):
"""computes the sum of a range of numbers
Precondition: lo <= hi
Postcondition: returns the sum of the numbers in the range [lo..hi]
Next, we write the unit test for it:
We run the unit test and it fails.
Next, we implement the body of
Now, run the tests. The tests indicate an assertion error, which points to a bug in the function logic. Fix the bug, and test again. (If you’re not sure what the bug is, try using Show in CodeLens and stepping through the code to help you figure it out.) Hint.
The range function generates numbers in the range
hi - 1. Our function should include
hi. Try adjusting the
hi parameter for range to
hi + 1.
Suppose we’re not creating a new function, but modifying an existing one. In Test-First Development, before making the modification to the function, we write a test for the new functionality. Then, we modify the function, and use the test to check that the modification worked.