Test names and readability
What’s in a Name?
It’s important to choose a useful name for a test. If tests are documentation, test names are the chapter headings.
Test Names Should Not Include Should
There’s nothing wrong with the word “should.” It’s just not necessary. It’s one more word to read when trying to understand a test. “It should divide by four” and “It divides by four” express the same idea in a test name.
it "foo should return the correct answer when passing in a date" do
end
There are several issues with the above. First of all, it contains should. Second, it runs roughshod over ‘it.’ If I read the line out loud, “it foo…” does not make sense. Also, that’s a lot of words. As a developer, I want to be able to skim the tests so that I can find the test case I’m looking for.
describe "foo" do
describe "when passing in a date" do
it "returns the correct value" do
end
end
end
Isn’t that better? In addition to being more readable, it makes it painfully obvious that we’re missing another test case. Let’s add it.
describe "foo" do
describe "when passing in a date" do
it "returns the correct value" do
end
end
describe "no date passed in" do
it "returns the correct value" do
end
end
end
Be Specific
Now we have another issue: two test cases called “returns the correct value.” Come to think of it, that’s also pretty generic. Probably most test cases could be called “it returns the correct value,” “it has the intended side-effects,” or “it has the intended side effects and returns the correct value.” The question is, how would you describe the value and/or side-effect we’re looking for? Not the actual value, but its significance or meaning.
describe "foo" do
describe "when passing in a date" do
it "returns the count of bars before the date" do
date = Date.today
assert_equal 2, foo(date)
end
end
describe "no date passed in" do
it "returns the count of all bars" do
assert_equal 4, foo
end
end
end
Cool. Now in this one-assertion test it doesn’t matter as much, but say we returned the bars themselves instead of the count and needed to make several assertions. Like test names, variable names in assertions improve readability.
expected_count = 2
expected_dates = ['2011-04-15', '2017-01-01']
result = foo
assert_equal expected_count, result.count
assert_equal expected_dates, result.map(&:date)
A Bit of Context
These tests are written for Minitest. If you’re using RSpec, you have an additional tool at your disposal: The context block.
describe "foo" do
context "when passing in a date" do
it "returns the count of bars before the date" do
end
end
end
Use describe
when specifying the subject of the test – the class or method. Use context
when talking about the circumstances under which we’re testing.
Takeaways
Let’s write some tests for our tests:
- it “begins with a verb, not a subject” (‘it’ is the subject)
- it “reads like a sentence” (‘it’ is the first word of the sentence)
- it “does not contain when or if” (those belong in describe blocks)
- it “is short” (and skimmable)
- it “doesn’t use should as the verb”
- it “is specific about the expected result” (what is the significance of the correct or expected result?)