Rspec :: Matcher'改变'接收器和消息与块的方法

时间:2015-01-26 17:12:40

标签: ruby-on-rails activerecord rspec factory-bot

我试图测试删除关联。涉及的两个模型是UserCancellation

    class Cancellation < Active Record::Base
      belongs_to :taker, class_name: "User"
    end

    class User < ActiveRecord::Base
      has_many :taken_cancellations, class_name: "Cancellation", foreign_key: :taker_id
    end

在我的测试中,我得到了以下代码:

describe "Admin Calendar" do
  subject { page } 

  let!(:user) { FactoryGirl.create(:user, name: "Taker User") }
  let(:cancellation) { FactoryGirl.create(:cancellation, start_at: 2.days.from_now, taker: user) }

  before do
    sign_in admin
    visit edit_admin_cancellation_path cancellation
  end

  #...

  describe "Edit Page" do
    it 'Making it available' do
      expect { click_link "Make it available" }.to change(cancellation.reload, :taker).from(user).to nil
    end
  end
end

click_link 'Make it available会在控制器中触发user.taken_cancellations.delete @cancellation

测试因此错误而失败:

taker should have been changed to nil, but is now #<User id: 1, name: "Taker User"...

来自let方法的指南: https://relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-let

  

该值将被缓存   在同一个示例中跨多个调用但不跨越示例。

对我而言,change方法在操作后创建新的cancellation而不是查看已记住的方法。我并不期待这种行为。

使用块时测试通过,如下所示:

expect { click_link "Make it available" }.to change { cancellation.reload.taker }.from(user).to nil

有人可以解释我是否正确吗?为什么这些不同的行为?

1 个答案:

答案 0 :(得分:2)

在第一种形式中,cancellation.reload被评估一次,结果对象在调用taker方法之前和之后发送消息click_link。换句话说,cancellation对象在调用click_link后重新加载

在第二种形式中,cancellation.reload.taker在调用click_link之前和之后进行全面评估,因此reload有机会在数据库更新发生后生效

请注意,根据您的配置,处理click_link结果的服务器可能在单独的线程中执行,因此无论如何在数据库更新和测试断言。