Stubbing Chained Methods with Rspec

后端 未结 5 1088
醉酒成梦
醉酒成梦 2021-02-05 22:06

I want to call a named_scope that will only return one record, but the named_scope returns an array, that\'s not a big deal as I can just chain it with .first:

M         


        
相关标签:
5条回答
  • 2021-02-05 22:58

    Better version of

    Client.stub!(:named_scope).and_return(@clients = mock([Client]))
    @clients.stub!(:first).and_return(@client = mock(Client))
    

    will be:

    Client.should_receive(:named_scope).with(param).and_return do
      record = mock_model(Comm)
      record.should_receive(:do_something_else)
      [record]  
    end
    
    0 讨论(0)
  • The question is quite old and hence there are few enhancements in how stubbing can be done. Now you can use stub_chain method to stub a chain of method calls. For example:

    @client = Client.named_scope(param).first

    can be stubbed with:

    Client.stub_chain(:named_scope,:first).and_return(@client = mock(Client))

    More examples of stub_chaining:

    describe "stubbing a chain of methods" do
      subject { Object.new }
    
      context "given symbols representing methods" do
        it "returns the correct value" do
          subject.stub_chain(:one, :two, :three).and_return(:four)
          subject.one.two.three.should eq(:four)
        end
      end
    
      context "given a string of methods separated by dots" do
        it "returns the correct value" do
          subject.stub_chain("one.two.three").and_return(:four)
          subject.one.two.three.should eq(:four)
        end
      end
    end
    

    or please have a look at:

    Long live the rspecs!!! :)

    0 讨论(0)
  • 2021-02-05 22:58

    I think you've already done the thin controller thing by putting the query into a named scope where it can be reused. Here is some code I used before I started using named scopes.

      def mock_comm(stubs={})
        @mock_comm ||= mock_model(Comm, stubs)
      end
    
      describe "responding to GET index" do
    
        it "should expose all comms as @comms" do
          Comm.should_receive(:find).with(:all).and_return([mock_comm])
          get :index
          assigns[:comms].should == [mock_comm]
        end
    # ...
    

    I would probably write code quite similar to what you have already, but maybe put it in a helper that allows me to reuse it. The other thing is to use a different mocking framework that maybe gives you more control. Have a look at Ryan Bates' railscast on RSpec - it's a bit old now but still some good ideas in there.

    0 讨论(0)
  • 2021-02-05 23:02

    I suppose this is in a controller spec?

    Your own suggestion should work fine. Another possibility is to move the named_scope call inside your model, to avoid the issue entirely. This would also be in line with the "fat models, thin controllers" advice.

    0 讨论(0)
  • 2021-02-05 23:05

    I figured something out.

    Client.stub!(:named_scope).and_return(@clients = mock([Client]))
    @clients.stub!(:first).and_return(@client = mock(Client))
    

    which allows me to call my controller:

    @client = Client.named_scope(param).first
    

    It works, but is there a better solution?

    EDIT:

    The release of rspec 1.2.6 allows us to use stub_chain meaning it can now be:

    Client.stub_chain(:named_scope, :chained_call).and_return(@clients = [mock(Client)])
    

    This was top of my head, as always check the api for specifics :)

    0 讨论(0)
提交回复
热议问题