0

The code samples below show a refactor from the chapter on controller specs in The RSpec Book:

require 'spec_helper'

describe MessagesController do
  describe "POST create" do
    it "creates a new message" do
      message = mock_model(Message).as_null_object
      Message.should_receive(:new).
        with("text" => "a quick brown fox").
        and_return(message)
      post :create, :message => { "text" => "a quick brown fox" }
    end

    it "saves the message" do
      message = mock_model(Message)
      Message.stub(:new).and_return(message)
      message.should_receive(:save)
      post :create
    end

    it "redirects to the Messages index" do
      post :create
      response.should redirect_to(:action => "index")
    end
  end
end

require 'spec_helper'

describe MessagesController do
  describe "POST create" do
    let(:message) { mock_model(Message).as_null_object }

    before do
      Message.stub(:new).and_return(message)
    end

    it "creates a new message" do
      Message.should_receive(:new).
        with("text" => "a quick brown fox").
        and_return(message)
      post :create, :message => { "text" => "a quick brown fox" }
    end

    it "saves the message" do
      message.should_receive(:save)
      post :create
    end

    it "redirects to the Messages index" do
      post :create
      response.should redirect_to(:action => "index")
    end
  end
end

I have a couple of questions:

1) I understand the benefit of using the let block because the tests for both creation and save use the mock_model. However, I don't understand the benefit of the before block. If only the save test requires the stub, why not just keep the code in the test instead of moving it to a before block that runs before each test?

2) More fundamentally, does the before block interfere with what the creation test is specifying? The creation test says that Message should receive new with some parameters and then tests that with the post :create. But if the before block just stubs out the call to new, doesn't that short circuit the should_receive assertion in the creation test? Perhaps I'm not understanding how stub and should_receive interact.

whiny_nil
  • 83
  • 2
  • 7

1 Answers1

0

If only the save test requires the stub

A typical create action would look like so:

def create
  @message = Message.new(params[:message])
  if @message.save
    # ... etc. ...

So yes, Message.new needs to be stubbed for each example.

If the before block just stubs out the call to new, doesn't that short circuit the should_receive assertion in the creation test?

Actually it works the other way around. The before block runs first and stubs out :new, then the message expectation runs and replaces the stub. You could remove the before block and put the stub at the beginning of the other two examples and get the same effect.

zetetic
  • 47,184
  • 10
  • 111
  • 119