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.
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]
"""
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.)
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.
Writing the tests first helps the programmer to clarify the function specification. It’s not possible to write an assert for a function that has a vague function docstring. This process forces the programmer to write a clear docstring and to practice specification-based testing, because when the tests are written, there is no function implementation to reference.
When the programmer writes the function and is ready to test it, the test is all ready to go. There is no internal struggle about whether a unit test should be written or not. The programmer runs the test, and gets instant feedback about whether the function is working or not.
If the function fails to pass the test, the benefits of unit testing in helping the programmer to quickly diagnose and fix the problem are instantly available. The test-debug cycle is rapid.
When a programmer modifies an existing function for which unit tests already exist, perhaps to add some more functionality, the existing unit tests serve as a safety net. They check that the modifications made by the programmer don’t break any of the old functionality.
The overall development time tends to be reduced. Perhaps counter-intuitively, writing more code (the unit tests) actually speeds up the overall development process, because of the benefits imparted by unit testing.
Believe it or not, there are psychological benefits. As the programmer works on the project, creating little tests and then writing code that passes those tests, there is a sense of accomplishment and satisfaction that comes every time a new test passes. Instead of spending hours of frustration debugging a new function in the context of a complex program, with few visible results, the test-first progress leads to more visible and regular successes.
Correct. Test-First Development requires the developer to write unit tests. However, these tests come with significant benefits and can speed up overall development.
Incorrect. Test-First Development requires the developer to write unit tests. However, these tests come with significant benefits and can speed up overall development.