One Expectation Per Spec

bdd

Sat Mar 15 14:17:00 -0700 2008

Jay Fields posts about one expectation per spec, something that I generally agree with but often find hard to practice. I typically find myself with one mock and one assert per test, such as this example from the rush specs:

it "transmits file_contents" do
  @con.should_receive(:transmit).with(:action => 'file_contents', :full_path => 'file').and_return('contents')
  @con.file_contents('file').should == 'contents'
end

I want to test both the input and the output - that the file_contents method calls the right method with the right arguments, and that it returns the expected value. Breaking this into two specs would be:

it "transmits file_contents" do
  @con.should_receive(:transmit).with(:action => 'file_contents', :full_path => 'file')
  @con.file_contents('file')
end

it "gets the right return value from file_contents" do
  @con.stub!(:transmit).and_return('contents')
  @con.file_contents('').should == 'contents'
end

This is a lot more verbose but I don’t feel it adds a whole lot of clarity. Checking both the input and the output in one place seems reasonable to me. But I’ll keep this in the back of my head and see how it influences my spec-writing.

Another item I spotted in Jay’s example specs is stub_everything. I wasn’t previously aware of this. (His examples use Mocha, but the RSpec mocks have the same exact method.) Like this:

class BankAccount
  def transfer(other_account, amount)
    balance -= amount
    other_account.balance += amount
  end
end

it "transfers money out of this account"
  @account.balance = 10
  @account.transfer(stub_everything, 1)
  @account.balance.should == 9
end

stub_everything returns an object that responds to every possible method, but does nothing on the calls. This allows you to effectively ignore any operations on that object, rather than having to stub every call explcitly.