RSpec上的错误行为期望更改计数与销毁

时间:2014-02-19 06:09:14

标签: ruby-on-rails ruby rspec

我希望用户只能删除他们自己创建的作业(而不是其他人的作业)。我已通过" rails server"验证我的应用确实这样做了。然而,测试结果很奇怪。

以下是有问题的测试:

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

除此之外,我得到了这个奇怪的输出:

故障:

  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)>'

为什么工作人数会增加?为什么测试不能按预期工作?


其他信息:

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

佣金路线

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
[...]

3 个答案:

答案 0 :(得分:5)

有两个问题:

  1. 正如Jacek所说,let被懒惰地评估。这意味着在使用变量之前,块中的代码不会运行。由于job块中首次使用expect变量,因此job计数会增加。如果您希望立即运行let块中的代码,请使用let!方法。

  2. getputpatchdelete应该用于控制器测试,而不是集成测试:

  3. # jobs_controller_spec.rb
    delete :destroy, id: job
    

    在集成测试中,您应该使用Capybara来模拟用户输入:

    # users_messing_around_with_jobs_spec.rb
    click_button("Delete")
    

    在您的测试中,delete没有达到您所期望的效果:由于某种原因,它已映射到jobs#index

答案 1 :(得分:3)

试试吧!而不是让因为简单的“让”是惰性评估的。我的意思是这两行:

let!(:job) { FactoryGirl.create(:job, user: user) }        
let!(:wrong_job) { FactoryGirl.create(:job, user: wrong_user) }

答案 2 :(得分:0)

让我们先检查我们的命名路线:

我们未找到delete操作的命名路由,因此delete job_path(wrong_job)delete job_path(job)不会按预期删除该作业。   相反,由于两个delete job_path(wrong_job)delete job_path(job)执行延迟评估了每个job块中的wrong_jobit,因此Job模型对象将始终更改每次测试为1。

有两种方法可以解决这个问题:

  1. delete操作创建命名路由。
  2. 添加ID以指定应删除哪个作业,例如:delete :destroy, id: job.id
  3. 首先发帖,如果不正确,请告诉我。