Specific interfaces – in the small

March 7, 2012 Stephan Hagemann

Everyone on the Web I found who states that quote I was looking for says “I don’t know who said it, but be ‘Generous on input, strict on output'” (or some variation on this). While I am unsure about the first proposition, I wholeheartedly agree with the second.

Edit 3/8: the quote is known in a different wording as Postel’s Law, which shows up as the Robustness Principle in RFC 793, the specification of TCP. Thanks for the hint, Austin!

Unfortunately, the closest a Rubyist typically gets to the implementation of an interface specification is his tests. This provides a pretty good, but somewhat disconnected specification that can sometimes cover up imprecisions in the interface’s implementation.

On top of that, sometimes our frameworks make it easy to forget what our tests are asserting or spec’ing.

Take rspec’s predicate matchers and this example:

require 'rspec/core'

class VeryImportantQuestions
  def self.really?(answer)
    answer == 'Yes. I am telling you.'
  end

  def self.really_really?(answer)
    answer == 'Yes. I am telling you.' ? 42 : nil
  end
end

describe "really?" do
  context "using rspec predicate matchers" do
    context "if someone is telling you" do
      it "should be really really the case and return true" do
        VeryImportantQuestions.really?('Yes. I am telling you.').should be_true
      end
    end
    context "if someone is not sure" do
      it "should return false" do
        VeryImportantQuestions.really?('I am not sure.').should be_false
      end
    end
  end
end

describe "really_really?" do
  context "using rspec predicate matchers" do
    context "if someone is telling you" do
      it "should be really really the case and return true" do
        VeryImportantQuestions.really_really?('Yes. I am telling you.').should be_true
      end
    end
    context "if someone is not sure" do
      it "should return false" do
        VeryImportantQuestions.really_really?('I am not sure.').should be_false
      end
    end
  end
end

be_true and be_false effectively hide the fact that what’s actually spec’ed is truthiness and falsiness. Only when the following context is added is this imprecision revealed:

  context "spec'ing the actual output of the method fails" do
    context "if someone is telling you" do
      it "should be really really the case and return true" do
        VeryImportantQuestions.really_really?('Yes. I am telling you.').should == true
      end
    end
    context "if someone is not sure" do
      it "should return false" do
        VeryImportantQuestions.really_really?('I am not sure.').should == false
      end
    end
  end

With regard to rspec, I suggest to consider twice whether the benefits of using specific matchers to not outweigh their benefits in your situation. You might get nicer test output, but you might lose the ability to immediately tell what you’re spec’ing.

With regard to tests in general: be specific about what you output – aka be specific about what you test.

Here is the gist: https://gist.github.com/1998462

About the Author

Biography

Previous
Portico is looking for a Web Application Developer
Portico is looking for a Web Application Developer

At Pivotal Labs, one of the services we provide our clients is helping them interview and hire. Pivotal Lab...

Next
Testing Ruby Services without Mocks
Testing Ruby Services without Mocks

There seems to be a tendency to stub or mock classes when writing integration tests for basic http services...