Как да тествате условно валидиране с rspec-rails 3.0.0 и shoulda-matchers 2.5.0

Изпълнявам приложение Rails 4 с rspec-rails 3.0.0 и shoulda-matchers 2.5.0 и имам модел на събитие, който има някои условни валидации като така:

class Event < ActiveRecord::Base
  validates :name, :location, :time, :city, :zipcode, :user, :state, presence: true
  validates :post_text, presence: true, if: :happened?
  validates :pre_text, presence: true, unless: :happened?

  belongs_to :community
  belongs_to :user
  belongs_to :state

  def happened?
    (self.time < Time.now) ? true : false
  end
end

Моят event_spec.rb изглежда така:

require 'spec_helper'

describe Event do
  before { @event = FactoryGirl.build(:future_event) }

  subject { @event }

  it { should validate_presence_of(:time) }
  it { should validate_presence_of(:name) }
  it { should validate_presence_of(:location) }
  it { should validate_presence_of(:user) }
  it { should validate_presence_of(:city) }
  it { should validate_presence_of(:state) }
  it { should validate_presence_of(:zipcode) }

  it { should belong_to(:state) }
  it { should belong_to(:user) }

  it 'has pre_text if future event' do
     expect(FactoryGirl.create(:future_event)).to be_valid
  end

  it 'has post_text if past event' do
    expect(FactoryGirl.create(:past_event)).to be_valid
  end
end

Но когато стартирам тестовете, моят it { should validate_presence_of(:time) } блок се проваля, защото продължава да изпълнява оставащите валидации и хвърля изключение за условните валидации, тъй като self.time е нула. Приложих хакерска корекция за това, като проверих time.present? в случилото се? метод. Но може ли някой да ми помогне да разбера кой би бил най-добрият метод за тестване на условни валидации, които изискват наличието на друго поле?


person Tyler    schedule 03.03.2014    source източник


Отговори (3)


Да, за съжаление няма друг начин. Факт е, че технически е позволено time да бъде нула, преди да запазите записа си за събитие, така че ако случайно се обадите #happened? преди time да бъде зададено, то ще предизвика изключение. Така че, ако напиша този код, аз лично бих се съгласил да поставя отметка в #happened?, защото това е случай, който така или иначе ще трябва да обработите (независимо от вероятността това наистина да се случи).

person Elliot Winkler    schedule 03.03.2014

Това определено идва късно, но за други, които търсят решение. Намерих ефективен начин да направя това:

context "if happened" do
  before { allow(subject).to receive(:happened?).and_return(true) }
  it { is_expected.to validate_presence_of(:time) }
end

Надявам се това да помогне

person El'Magnifico    schedule 26.07.2015
comment
Това е правилният отговор и трябва да бъде приет. - person Midwire; 17.06.2016
comment
Това не ми се струва правилно. Подиграваш се на нещо, а след това очакваш резултатът да е верен. Разбира се, че ще е истина, така се подиграхте. Вие не тествате логиката, вие тествате макет, нали? - person emptywalls; 13.07.2016
comment
@emptywalls, разбираш ли контекста на въпроса на OP? Той не тества метода :happened?. Той иска да тества валидността на присъствието на времето. Времето в този случай присъства само когато събитието се е случило. За да ограничите очакванията на теста за валидиране до момента, в който събитието се е случило (когато :happened? върне true). Човек трябва да се подиграе с happened?, за да върне true, така че validate_presence_of(:time) да бъде извикан само с това условие. Така че, аз само се подиграх на условие, че валидирането зависи от това кое е точно това, от което се нуждае OP. - person El'Magnifico; 13.07.2016

Само малък съвет:

def happened?
  (self.time < Time.now) ? true : false
end

е същото като

def happened?
  self.time < Time.now
end
person franciscomxs    schedule 08.10.2014