This post originated from an RSS feed registered with Ruby Buzz
by Jeremy Voorhis.
Original Post: Is Your Test Suite Acceptable?
Feed Title: JVoorhis
Feed URL: http://feeds.feedburner.com/jvoorhis
Feed Description: JVoorhis is a Rubyist in northeast Ohio. He rambles about Ruby on Rails, development practices, other frameworks such as Django, and on other days he is just full of snark.
Is your test suite acceptable? I mean acceptable in the Chomsky sense of the word. Here are a few heuristics:
Do you understand your test at first glance?
Do you understand the code that is being exercised after reading your test?
Do your tests tend to have 1 assertion or 10 assertions? More?
Do your tests have cryptic names, like test_component?
Are you using external test fixtures? If so, how much time do you spend reading and cross-referencing them?
If you are using external test fixtures, how well do they serve your needs? Are you creating tests that require you to cross reference external fixtures and temporary objects?
Here are some design principles that are useful for lowering the mental overhead your test suite requires:
Give your tests meaningful names
If you can accurately infer what your test is trying to accomplish by reading its method name, you can understand it much faster. It’s even more helpful when that name appears in a report generated by a test runner!
Favor acceptability over DRYness
Which is worse: repeating a string literal two or three times within a test, or losing time reading the test later? Of course, this is a tradeoff, and both sides have cumulative effects.
Group your tests according to their context
Just because all of your tests exercise the same class, they don’t all have to live together. If you are testing a Stack class, don’t be afraid to separate your tests for an empty stack and your tests for a full stack. You can then use the setup method to instantiate the kind of stack you are testing, and your tests will be easier to understand. Conversely, when tests that are grouped together rely on different contexts, you will spend longer establishing your mental model when you read these tests later.
Write individual tests for a method’s individual behaviors
Rather than writing one test for one method, try analyzing the expectations you are placing on your method. Let’s use a method that accepts a blob, generates a filename and saves the blob to the named file as an example. Write one test to ensure the file name is generated correctly, and write another test to ensure the contents on disk are correct.
Keep your tests short
If you can use setup or some other means to create all the context you need, and your tests target individual behaviors, you can write a suite of tests that is very scannable by human eyes.
Avoid external fixtures when feasible
Relying on external fixtures may cause you to spend much time cross-referencing your test against many fixture files. What’s worse, changing an external fixture for one test may have the side effect of breaking other tests!