Метод «изменения» Rspec::Matcher с получателем и сообщением против блока

Я пытаюсь проверить удаление ассоциации. Используются две модели User и Cancellation:

    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

Кто-нибудь может объяснить, прав ли я? Почему такое разное поведение?


person felix    schedule 26.01.2015    source источник


Ответы (1)


В первой форме cancellation.reload оценивается один раз, и результирующий объект отправляет сообщение taker до и после вызова метода click_link. Другими словами, объект cancellation не перезагружается после вызова click_link.

Во второй форме cancellation.reload.taker оценивается полностью до и после вызова click_link, поэтому reload имеет шанс вступить в силу после обновления базы данных.

Однако обратите внимание, что в зависимости от вашей конфигурации сервер, обрабатывающий результат click_link, может выполняться в отдельном потоке, поэтому в любом случае возникает состояние гонки между обновлением базы данных и тестовым утверждением.

person Peter Alfvin    schedule 28.01.2015
comment
Имеет смысл. Спасибо! - person felix; 28.01.2015