Очистить сопоставитель Rspec для change(Model, :count).by(1)

Я усердно работаю, пытаясь сохранить свои файлы спецификаций как можно более чистыми. Использование гема «shoulda» и написание настраиваемых сопоставителей, которые следуют одному и тому же шаблону.

Мой вопрос касается создания пользовательского сопоставителя, который будет обертывать expect{ post :create ... }.to change(Model, :count).by(1) и может использоваться в тех же группах примеров с другими сопоставителями «следует». Подробности ниже:

Пользовательский сопоставитель (упрощенный)

RSpec::Matchers.define :create_a_new do |model|
  match do |dummy|
    ::RSpec::Expectations::ExpectationTarget.new(subject).to change(model, :count).by(1)
  end
end

Рабочий пример

describe 'POST create:' do
  describe '(valid params)' do
    subject { -> { post :create, model: agency_attributes } }
    it { should create_a_new(Agency) }
  end
end

Это работает нормально, пока я использую subject лямбда, а мой сопоставитель — единственный в группе примеров.

Неудачные примеры

Неудачный пример 1

Добавление большего количества примеров в ту же группу приводит к сбою другого сопоставителя, потому что subject теперь является лямбдой, а не экземпляром контроллера.

describe 'POST create:' do
  describe '(valid params)' do
    subject { -> { post :create, model: agency_attributes } }
    it { should create_a_new(Agency) }
    it { should redirect_to(Agency.last) }
  end
end

Неудачный пример 2

Сопоставитель «должен» ожидать, что я определю блок before, но это становится несовместимым с моим пользовательским сопоставителем

describe 'POST create:' do
  describe '(valid params)' do
    before { post :create, agency: agency_attributes }
    it { should create_a_new(Agency) }
    it { should redirect_to(Agency.last) }
  end
end

Ожидаемый результат

Я ищу способ написать свой собственный сопоставитель, который поместился бы в той же группе примеров, что и другие сопоставители, что означает, что мой пользовательский сопоставитель должен использовать блок before для выполнения действия контроллера, «неудачный пример № 2» выше - это то, как я хотел бы написать свои характеристики. Является ли это возможным?

Спасибо за прочтение


person Benj    schedule 02.08.2013    source источник


Ответы (1)


Я не думаю, что есть способ, которым вы можете передать свои неудачные примеры.

Это связано с тем, что change действительно нуждается в лямбда-выражении, поскольку ему нужно выполнить ваш подсчет дважды (один раз до и один раз после его вызова). Вот почему я стараюсь не использовать его (или использовать в изоляции от контекста).

Что я обычно делаю, вместо того, чтобы использовать сопоставитель count, проверяю три вещи:

  • Запись сохраняется. Если я назначаю модель @model, то использую expect(assigns(:model)).to be_persisted
  • Запись является экземпляром ожидаемой модели (хотя она может показаться бесполезной, но она достаточно информативна при использовании STI). expect(assigns(:model)).to be_a(Model).
  • Убедитесь, что последняя запись в БД такая же, как та, которую я только что создал `expect(assigns(:model)).to eq(Model.last)``

Именно так я обычно тестирую change сопоставитель, не используя его. Конечно, теперь вы можете создать свой собственный сопоставитель

RSpec::Matchers.define :create_a_new do |model|
  match do |actual|
    actual.persisted? &&
      actual.instance_of?(Participant) &&
      (Participant.last == actual)
  end
end
person Serabe    schedule 04.08.2013
comment
Интересно, спасибо за разъяснение. Позвольте мне проверить это немного, и я вернусь с вами. - person Benj; 05.08.2013
comment
Еще одна вещь, которую я пытаюсь в последнее время, - это инкапсулировать действие (post :create, params, session) в метод. Пока не уверен, буду ли я продолжать это делать, но в некоторых случаях это может стоить того. - person Serabe; 05.08.2013
comment
Это тоже была одна из моих попыток set(:action) {post :create ...}, но я не нашел способа быть действительно СУХИМ, потому что в итоге получил вонючий код вроде before { unless example.metadata[:skip_before]; action end } - person Benj; 05.08.2013
comment
Есть способ с хуками around, но это будет означать запуск ожидания в каждом примере, и я не думаю, что это то, что вам нужно. - person Serabe; 06.08.2013
comment
Извините, что отнял у меня время, чтобы ответить, я еще не проверял, но кажется, что это правильно, я проверю это позже, когда найду время для этого. Спасибо за совет - person Benj; 11.08.2013