Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
RSpec formats and the Single Responsibility Principle

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

Comments
  1. Mike Gehard says:

    Thanks for posting this Matthew. I’ve been having these same thoughts about my tests and glad to see that others are feeling the same things.

  2. Nigel Pepper says:

    I agree with what you’re saying here. I often find method ‘spec’ style testing draws your focus to the wrong thing at times whereas describing the object, as you say, maintains focus on it’s responsibility.

    I suspect most tests written in the method-spec style are as a result of many rspec examples being laid out in that format to start with.

  3. Chad Woolley says:

    Thanks for this insight.

    However, this approach also leads you to redundantly and unnecessarily test the same behavior in different contexts (i.e. when the actual code would not behave any differently under the different contexts).

    This in turn leads to bloated and slow spec suites. Do you care on a greenfield project? Maybe not. But I’m coming from the perspective of the 7-year-old 10000+ example Tracker Rails codebase. But that’s a good problem to have, I guess ;)

    — Chad

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *