RSpec formats and the Single Responsibility Principle

November 3, 2013 Matthew Parker

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).

About the Author

Matthew Parker

Matt Parker is Head of Engineering for Pivotal Labs

More Content by Matthew Parker
Previous
DC.js for D3-Based Data Visualizations
DC.js for D3-Based Data Visualizations

A recent, shorter-term project prompted us to search for a library that would let us rapidly develop featur...

Next
Messages Not Types: Exploring Ruby's Conversion Protocols
Messages Not Types: Exploring Ruby's Conversion Protocols

Duck typing is a style of programming that relies on what an object does, rather than on what it is. Avoid...

Enter curious. Exit smarter.

Register Now