Wrong behavior on RSpec expect change count with destroy
rspec, ruby, ruby-on-rails
Solution
There are two problems:
As Jacek mentioned, `let` is evaluated lazily. That means that the code inside the block isn't run until the variable is used. Since the `job` variable is used for the first time within the `expect` blocks, the `job` count increases. If you want the code inside the `let` blocks to be run immediately, use the `let!` method.
`get`, `put`, `patch`, and `delete` should be used in controller tests, not integration tests:
# jobs_controller_spec.rb
delete :destroy, id: job
In integration tests, you should use Capybara to simulate user input:
# users_messing_around_with_jobs_spec.rb
click_button("Delete")
In your tests, `delete` doesn't do what you'd expect: for some reason it's mapped to `jobs#index`.
Problem
I'm looking to have users only be able to delete the jobs that they've created themselves (not the jobs of others). I've verified that my app does do this through "rails server". However, the tests are coming out weird. Here are the tests in question: ``` require 'spec_helper' describe "Authentication" do subject { page } describe "signin" do [...] describe "authorization" do [...] describe "correct user control" do let(:user) { FactoryGirl.create(:user) } let(:job) { FactoryGirl.create(:job, user: user) } let(:wrong_user) { FactoryGirl.create(:user, email: "wrong@example.com") } let(:wrong_job) { FactoryGirl.create(:job, user: wrong_user) } before { sign_in user } [...] describe "users can only delete their own jobs" do it "should not change job count" do expect do delete job_path(wrong_job) end.to_not change(Job, :count) end end describe "users can delete their own jobs" do it "should decrease job count" do expect do delete job_path(job) end.to change(Job, :count).by(-1) end end end end end end ``` Except, I'm getting this weird output: Failures: ``` 1) Authentication signin authorization correct user control users can only delete their own jobs should not decrease job count Failure/Error: expect do count should not have changed, but did change from 0 to 1 # ./spec/requests/authentication_pages_spec.rb:68:in `block (6 levels) in <top (required)>' 2) Authentication signin authorization correct user control users can delete their own jobs should decrease job count Failure/Error: expect do count should have been changed by -1, but was changed by 1 # ./spec/requests/authentication_pages_spec.rb:75:in `block (6 levels) in <top (required)>' ``` Why does the job count increase? Why does the test not work as intended? Other information: jobs_controller.rb ``` class JobsController < ApplicationController skip_before_action :require_signin, only: [:index, :show] skip_before_action :correct_user, only: [:index, :show, :new, :create] before_action :set_job, only: [:show, :edit, :update, :destroy] [...] def destroy @job.destroy respond_to do |format| format.html { redirect_to jobs_url } format.json { head :no_content } end end private def set_job @job = Job.find(params[:id]) end def job_params params.require(:job).permit(:title, :org, :internship, :postdate, :filldate, :location, :link, :description) end end ``` application_controller.rb ``` class ApplicationController < ActionController::Base protect_from_forgery with: :exception before_filter :require_signin before_filter :correct_user include SessionsHelper private def require_signin unless signed_in? store_location redirect_to signin_url, notice: "Please sign in." end end def correct_user @job = current_user.jobs.find_by(id: params[:id]) redirect_to root_url if @job.nil? end end ``` rake routes ``` Prefix Verb URI Pattern Controller#Action jobs GET /jobs(.:format) jobs#index POST /jobs(.:format) jobs#create new_job GET /jobs/new(.:format) jobs#new edit_job GET /jobs/:id/edit(.:format) jobs#edit job GET /jobs/:id(.:format) jobs#show PATCH /jobs/:id(.:format) jobs#update PUT /jobs/:id(.:format) jobs#update DELETE /jobs/:id(.:format) jobs#destroy [...] ```