Tuesday, May 1, 2012

How Do You Name Your Tests?

How do you name your classes? You probably think of the noun or verb which best describes the behavior you want; perhaps there are some technical details which slip through like the name of a design pattern. But generally, a class' name should tell you what that class is responsible for. (Edit: Jeff Langr and Tim Ottinger have written an excellent article on the mechanics of how to name your tests.)

Methods are easier to name, perhaps because we do it so much more often. Usually, you'll decide that the method is important because of the return value or the side effects on the enclosing class. Then try to communicate that in as few characters as possible.

Test fixtures and methods are different. A test name doesn't communicate what the test does, it communicates what SOMETHING ELSE does.

Let's look at some common naming conventions used in tests. Our System Under Test will be a Video Store billing system.

TEST_F(BillingTest, WhenRentingARegularMovie_GoodCustomerGetsOneFrequentRenterPoint)
TEST_F(BillingTest, CustomerWithNoLateFeesGetsOneFrequentRenterPointWhenRentingOneRegularMovie)

These two examples are pretty standard fixture names. Specifically, they just tell you the system being tested at the macro level. This may be common at the early stage of TDD because you haven't extracted subsystems yet. This is expected to change as development continues.

TEST_F(CustomerTest, WhenRentingARegularMovieGoodCustomerGetsOneFrequentRenterPoint)

This example names the fixture after the subsystem being tested. There's duplication between the test method name and the fixture name because there's a subset of Customers which we're concerned with. This will happen eventually to every test named this way.

TEST_F(GoodCustomerTest, WhenRentingARwegularMovieGetsOneFrequentRenterPoint)

This example learned a small lesson, and named the fixture after the general condition of the subsystem, thus eliminating the duplication.

TEST_F(CustomerFrequentRenterPointTest, WhenRentingARegularMovieGoodCustomerGetsOne)

This example tries to describe the effect as if it were the system under test. It is common to confuse the two, but it confuses the writer of the tests as much as the reader.

TEST_F(CustomerInGoodStandingRentingOneRegularMovie, GetsOneFrequentRenterPoint)

The last example demonstrates two features of good naming. The first derives from the fact that the fixture is used to share setup between tests, and so can be explicit about what is being shared.

The second feature is that the test method tells you what behavior to expect before the condition under which to expect it. This puts the emphasis of the test on the property of the system which the test is exercising. You would expect to see tests in the same fixture to be easy to compare against eachother. For example:

TEST_F(CustomerInGoodStandingRentingOneRegularMovie, GetsOneFrequentRenterPoint)
TEST_F(CustomerInGoodStandingRentingOneRegularMovie, Pays1_95ForOneDay)
TEST_F(CustomerInGoodStandingRentingOneRegularMovie, Pays2_95ForTwoDays)

Tests document our systems' expected behavior, and names play an important part of that. You want names which help navigate through that documentation. Put the most important information about your system at the first place the next coder will look: the beginning of the test method name. And if you've got underscores in your name to separate the parts, you're probably doing it wrong.

No comments:

Post a Comment