开发者

Stubbing named_scope in an RSpec Controller

I haven't been able to find anything for a situation like this. I have a model which has a named scope defined thusly:

class Customer < ActiveRecord::Base
  # ...
  named_scope :active_customers, :conditions => { :active => true }
end

and I'm trying to stub it out in my Controller spec:

# spec/customers_controller_spec.rb
describe CustomersController do
  before(:each) do
    Customer.stub_chain(:active_customers).and_return(@customers = mock([Customer]))
  end

  it "should retrieve a list of all customers" do
    get :index
    response.should be_success
    Customer.should_receive(:active_customers).and_return(@customers)
  end
end

This is not working and is failing, saying that Customer expects active_customers but received 开发者_如何学JAVAit 0 times. In my actual controller for the Index action I have @customers = Customer.active_customers. What am I missing to get this to work? Sadly, I'm finding that it's easier to just write the code than it is to think of a test/spec and write that since I know what the spec is describing, just not how to tell RSpec what I want to do.


I think there's some confusion when it comes to stubs and message expectations. Message expectations are basically stubs, where you can set the desired canned response, but they also test for the call to be made by the code being tested. In contrast stubs are just canned responses to the method calls. But don't mix a stub with a message expectation on the same method and test or bad things will happen...

Back to your question, there are two things (or more?) that require spec'ing here:

  1. That the CustomersController calls Customer#active_customers when you do a get on index. Doesn't really matter what Customer#active_customers returns in this spec.
  2. That the active_customers named_scope does in fact return customers where the active field is true.

I think that you are trying to do number 1. If so, remove the whole stub and simply set the message expectation in your test:

describe CustomersController do
  it "should be successful and call Customer#active_customers" do
    Customer.should_receive(:active_customers)
    get :index
    response.should be_success
  end
end

In the above spec you are not testing what it returns. That's OK since that is the intent of the spec (although your spec is too close to implementation as opposed to behavior, but that's a different topic). If you want the call to active_customers to return something in particular, go ahead and add .and_returns(@whatever) to that message expectation. The other part of the story is to test that active_customers works as expected (ie: a model spec that makes the actual call to the DB).


You should have the array around the mock if you want to test that you receive back an array of Customer records like so:

Customer.stub_chain(:active_customers).and_return(@customers = [mock(Customer)])


stub_chain has worked the best for me.

I have a controller calling

ExerciseLog.this_user(current_user).past.all

And I'm able to stub that like this

ExerciseLog.stub_chain(:this_user,:past).and_return(@exercise_logs = [mock(ExerciseLog),mock(ExerciseLog)])
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜