What makes a good unit test?
Motivating rant
There are numerous articles out there talking about how to write unit tests. Still, I find that they mostly assume that the reader is an accomplished developer trying to learn to write unit tests.
In particular, many articles, such as this one from the force.com people, may have the right idea, but atrocious examples. In the linked article, the getFraction() example doesn't test the behaviour of a unit, it tests the division operation of the processor! Not only is this a fairly unlikely failure, it is also highly specific to the processor the unit test is run on, and doesn't say whether it is going to work on any other processor the code is need to work on.
For those who are trying to learn programming, a somewhat different approach is needed. This article tries to find an approach that work in a situation where you are trying to learn programming with a test-driven approach.
What is a "test"?
Technically, a test is a small program that tries to make certain that some part of the production software performs as expected.
Simply put, each test should tell a story. This story contains
- a protagonist: it may be a procedure, method, class or an object,
- (usually) supporting characters: these are typically mocked objects or methods, and
- an interesting story: this is usually a use case for a part (the so-called "unit") of the system.
So what is a "unit"?
Most people ask this question when they try to make sense of test-driven development. The traditional answer is that the unit is, in Wikipedia's terms "the smallest testable part of an application." The problem is that when we write the test before the implementation, the choice of a unit is a side-effect of the narrative of the test and so does not require particular consideration.
So what about this story?
Without further ado:
public void testWolfCanWasteHousesButNotThatOfPracticalPiglet() { Wolf wolf = new Wolf(); Piglet fifer = Hamlet.lookupPiglet("Fifer"); assert !fifer.isMason(); // sanity check House house = fifer.getHouse(); wolf.huffAndPuff(house); assert house.isRuined(); assert house.getDoor().isOpen(); Piglet practical = Hamlet.lookupPiglet("Practical"); assert practical.isMason(); house = practical.getHouse(); wolf.huffAndPuff(house); assert !house.isRuined(); assert !house.getDoor().isOpen(); }
This test documents the contract between a Wolf and various piglets. Basicly, it says that a Wolf can huff and puff and blow the house down, unless the house is built by a mason.
Depending on programming language and development environment, exactly how to execute a test varies. There are numerous articles detailing this for various programming language and environment. Search engines are your friends.
Tests should be named for what they test. The ultimate goal of the test name is so that a developer immediately understands contract has been broken.
Finally
Finally, there are some fine articles about unit testing out there.
- Log in to post comments