I'm testing the index
action for my ProjectsController
.
I'm using the will_paginate gem, and am trying to write an RSpec test that ensures the paginate method is called on the current user's projects when they projects_path.
The result I'm getting, however, isn't what I expected and I can't figure out why.
result
Failure/Error: expect(user.projects).to receive(:paginate)
(#<ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_Project:0x00000004719ef0>).paginate(any args)
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/requests/project_pages_spec.rb:82:in `block (3 levels) in <top (required)>'
projects#index
def index
if params[:search]
@projects = current_user.projects.search(params[:search]).paginate(:page => params[:page], :per_page => 13)
else
@projects = current_user.projects.paginate(:page => params[:page], :per_page => 13)
end
end
index test
describe "index" do
describe "pagination" do
let(:user) { FactoryGirl.create(:user) }
let(:client) { FactoryGirl.create(:client) }
before do
capybara_sign_in(user)
@project = Project.create(name: "Blog", fee: 550, client_id: client.id)
end
it "should call the paginate method" do
expect(user.projects).to receive(:paginate)
visit projects_path
end
end
end
Note that I haven't finished writing the tests, so please omit any comments re: drying up the code etc.
Thanks for the help guys/gals! :)
The reason it is failing is that user
in your spec file is not the same as current_user
in your controller. So while current_user
is receiving paginate
your spec's user
never is. Couple of ways you could solve it.
You could do this:
expect_any_instance_of(User).to receive(:paginate)
Drawback here is that you're testing for any instance, not specifically your current_user
instance. Whether that's a problem or not is up to you :)
The other way would be to stub current_user
:
controller.stub(:current_user).and_return(user)
expect(user.projects).to receive(:paginate)
Upside is you're testing exactly the same user. Downside is stubbing current_user
might introduce other issues.
Part of the pain you are feeling is that you are testing implementation instead of behavior. In general, the behavior of a web request can be described in a few words: it accepts input parameters and returns a response. To test the behavior, test the contents of the response.
In your case, the app will behave one way when params[:search]
is present, and another way when it is missing. So write one test with params[:search]
set to an appropriate value, and one with it missing, and test that the response is what is expected in each case. You don't need to check the entire response, just enough to verify that the right data is returned.
To make the tests return the right responses, use a different set of data for each test. The model will return the expected rows, and voilà!, passing tests.
Now this may seem like more work than mocking and stubbing, and it will take more time to run, but it is the right way to test a request. You want to check the whole stack, not just the method call in the controller. The resulting tests will be true integration tests, and will be less brittle since they are not coupled to the implementation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With