Last year I read Brian Button's wonderful article "Double Duty" in Better Software magazine (the February, 2005 issue). One of the things I learned is that Brian is the world's best namer of unit tests. I visited Brian's web site for more of his ideas and found an article called "TDD Defeats Programmer's Block—Film at 11." In this article, Brian describes using the Test Driven Development process to write a "continuous integration system" (a tool that automatically (re)builds software systems when programmers change the source code). Here are some examples of his unit test names:
- Starting Build With No Previous State Only Starts Build For Last Change
- Previous Build Number Is Incremented After Successful Started Build
- Last Build Failing Leaves Last Build Set To Previous Build
What makes these names so good? I analyzed a few dozen of Brian’s test names and found this pattern: stimulus and result in context. Let’s examine these names to identify the parts.
Starting Build With No Previous State Only Starts Build For Last Change:
- Context: There is no previous state (i.e. no previous builds were done).
- Stimulus: Start a build.
- Result: A build was started for only the last change.
Previous Build Number Is Incremented After Successful Started Build:
- Context: There were zero or more previous builds.
- Stimulus: Request a build that will succeed.
- Result: The build number is one more than before the build.
Last Build Failing Leaves Last Build Set To Previous Build:
- Context: There were previous builds, the most recent of which is recorded in the system as the last build.
- Stimulus: Request a build that will fail.
- Result: The previously identified last build is still identified as the last build.
One of Brian’s tests from a different system—an “animal factory” (a concept better left unexplained)—is called Default Animal Is Cow.
- Context: No animal type has been identified as the desired type of animal for the system to manufacture.
- Stimulus: Request that the system manufacture an animal.
- Result: A new cow exists.
Now that I’ve learned the pattern that makes Brian’s test names so useful, I can use it deliberately. Using the context-stimulus-result scheme increases the value of tests as documentation. The resulting names make clear what specifically is being tested and under what specific conditions. This helps the reader to understand quickly what each test does, and what is covered by each set of tests.
Another benefit is that the context-stimulus-result naming scheme encourages you to clarify your thinking about each test. Each unit test establishes some set of starting conditions, or context. Each stimulates the system. Each compares the result to a desired result. In order to name these elements you will have to think about the specifics of each and clarify them well enough that you can describe each in a few words.
If you’re having difficulty naming a test using this scheme, that may indicate a problem in your test. Perhaps the test is doing too much work, or your test suite is doing too little. For example, suppose you’re testing software to manage bank accounts, and one test is called Withdrawal Test. We can tell from this name that the test tests the withdrawal feature in some way. But we don’t know what specific aspects of withdrawals this test is testing.
Does Withdrawal Test test only that a withdrawal of less than the account balance reduces the balance by the proper amount? If so, calling this test “Withdrawal Test” may indicate that your suite of tests for the withdrawal feature is missing many important test cases. The name of the test gives readers an overly broad sense of what the test actually tests.
Does Withdrawal Test test a score of different stimuli under a dozen different conditions? If so, it’s probably doing too much work. The name of the test does not quickly tell readers what is being tested.
Whether Withdrawal Test is doing too much work or too little, we can improve the test by applying the context-stimulus-result scheme. If Withdrawal Test is doing too much, we can use the scheme to identify how to break the test into smaller, more focused tests with more descriptive names. If Withdrawal Test tests only one tiny aspect of withdrawals and leaves other aspects untested, we can use the scheme to create a better name for the test and to identify other tests to write.