For years, my unit tests have largely followed the “method spec” format:
describe SomeObject do
describe "#some_method" do
context "in some context" do
it "does something"
end
context "in some other context" do
it "does something else"
end
end
describe "#some_other_method" do
context "in some context" do
it "does something"
end
context "in some other context" do
it "does something else"
end
end
end
But I have a theory: this approach to testing encourages violations of the single responsibility principle.
Our objects model our domain; they have specific behaviors and act on specific state. They’re not just random bags of methods. The methods on a model should make sense together. They should all reflect that object’s responsibility.
The problem with the “method spec” format is that it’s entirely possible to add another method to the model without thinking about all of the other methods on the model. And when you don’t have to think about all of the other methods on the model, you don’t have to stop and think about this model’s responsibility. And pretty soon, your model is modelling many things, and exhibiting many responsibilities. Testing gets harder, object reuse declines, brittleness abounds, and then: lions, tigers, bears.
So, lately, I’ve tried to make my tests make me think about my object’s responsibilities. Instead of breaking my unit test file down into methods, I break it down into the object’s contexts:
describe HumanPopulation do
context "in the jurassic period" do
it "is empty"
end
context "in the 20th century" do
it "has people of all ages"
end
context "in the near future" do
it "is destroyed by skynet"
end
end
end
My first level of organization inside my object’s spec is the object’s context itself. Inside of a context, I then spec out the behavior of the object. And as I add new methods onto an object, I’m forced to think more about the object’s responsibility. This approach to testing also lends itself well to constructor injection.
Note: if you’re a rails developer, there are certainly places where a “method spec” approach makes much more sense (e.g., helper specs).