Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrong behavior on RSpec expect change count with destroy

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: "[email protected]") }
        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
[...]
like image 927
peterhurford Avatar asked Oct 23 '25 18:10

peterhurford


2 Answers

There are two problems:

  1. 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.

  2. 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.

like image 176
Patrick Brinich-Langlois Avatar answered Oct 26 '25 09:10

Patrick Brinich-Langlois


Try let! instead of let because simple "let" is lazy-evaluated. I mean especially this two lines:

let!(:job) { FactoryGirl.create(:job, user: user) }        
let!(:wrong_job) { FactoryGirl.create(:job, user: wrong_user) }
like image 36
Jacek Avatar answered Oct 26 '25 08:10

Jacek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!