2011-12-19 102 views
4

我現在在Shoulda和Rspec之間搖擺。我已經閱讀並且在RSpec上播放了一些,但與Shoulda沒有多大關係。我發現Shoulda的一行斷言更容易閱讀,測試看起來更清晰。但是當我無法弄清楚在Shoulda中如何編寫特定的斷言時,我將切換到RSpec。雖然不是很高興。Rails 3自定義驗證和shoulda

所以這就是我今天所做的。我爲我的模型Course寫了一些自定義驗證。一個課程有一個start_date和一個end_date.有一些規則。

  • start_dateend_date都是強制性
  • start_date不能遲於今天
  • end_date不能前start_date

我知道有安靜的幾個寶石,在那裏,可以做它對我來說。但是,因爲我是新的,我認爲這可能是一個好主意,我自己做,並隨着我的學習而去學習。

所以這是我的模型是什麼樣子

class Course < ActiveRecord::Base 
    belongs_to :category 
    has_many :batches, :dependent => :destroy 
    accepts_nested_attributes_for :batches, :reject_if => lambda {|a| a[:code].blank?}, :allow_destroy => true 
    has_and_belongs_to_many :students, :uniq => true 

    validates_presence_of :name, :course_code, :total_seats 
    validates_uniqueness_of :category_id, :scope => [:name, :course_code] 

    validates :start_date, :presence => true, :course_start_date=>true 
    validates :end_date, :presence => true, :course_end_date=>true 
end 

我的自定義校驗都是如下

class CourseEndDateValidator < ActiveModel::EachValidator 
    def validate_each(object, attribute, value) 
    if object.errors[attribute].blank? && object.errors[:start_date].blank? 
     if value < object.start_date 
     object.errors[attribute] << "cannot be later than start date" 
     end 
    end 
    end 
end 

class CourseStartDateValidator < ActiveModel::EachValidator 
    def validate_each(object, attribute, value) 
    if object.errors[attribute].blank? 
     if value < DateTime.now.to_date 
     object.errors[attribute] << "cannot be later than today" 
     end 
    end 
    end 
end 

而以下是我course_spec

require 'spec_helper'require 'date' 

describe Course do 

    context 'validations' do 
    it { should validate_presence_of(:name)} 
    it { should validate_presence_of(:course_code)} 
    it { should validate_presence_of(:start_date)} 
    it { should validate_presence_of(:end_date)} 
    it { should validate_presence_of(:total_seats)} 

    date = DateTime.now.to_date 
    it { should allow_value(date).for(:start_date) } 
    it { should_not allow_value(date - 10).for(:start_date) } 
    it {should allow_value(date + 10).for(:end_date)} 
    end 

    context 'associations' do 
    it { should belong_to(:category)} 
    it { should have_many(:batches).dependent(:destroy)} 
    it { should have_and_belong_to_many(:students) } 
    end 

    it " end date should not be before course start date" do 
    course = FactoryGirl.build(:course, :end_date=>'2011-12-10') 
    course.should be_invalid 
    end 
end 

現在擺在我寫的最後「它」阻止使用Rspec我在我的驗證上下文中有這樣的東西

context 'validations' do 
    it { should validate_presence_of(:name)} 
    it { should validate_presence_of(:course_code)} 
    it { should validate_presence_of(:start_date)} 
    it { should validate_presence_of(:end_date)} 
    it { should validate_presence_of(:total_seats)} 

    date = DateTime.now.to_date 
    it { should allow_value(date).for(:start_date) } 
    it { should_not allow_value(date - 10).for(:start_date) } 
    it { should allow_value(date + 10).for(:end_date)} 
    it { should_not allow_value(date - 10).for(:end_date)} # <------------------- 
    end 

而且我得到了以下故障

Failures: 

    1) Course validations 
    Failure/Error: it { should_not allow_value(date - 10).for(:end_date)} 
     Expected errors when end_date is set to Fri, 9 Dec 2011, got errors: ["name can't be blank (nil)", "course_code can't be blank (nil)", "total_seats can't be blank (nil)", "start_date can't be blank (nil)"] 

我不知道我在做什麼錯在這裏。是我的自定義驗證代碼不正確,或者我需要在運行最後一個斷言之前設置一些東西,以便在測試end_date時start_date不爲零?

驗證在視圖中正常工作。我的意思是,根據我輸入的數據類型,我得到了正確的驗證錯誤。但是我的測試失敗了。我一直在看這一段時間,但無法弄清楚我到底做錯了什麼。

回答

0

@nickgrim已經回答了這個問題,但我想添加一條評論。 describeit的要點是鼓勵以「describe」和「it」開頭的句子。在你的例子中,你有這個:

it " end date should not be before course start date" do 
    # ... 

「it end date ....」不是一個句子。請寫,就像這樣:

it "validates that end date should be >= start date" do 
+0

感謝您的輸入,但我認爲當在控制檯上打印出步驟時,他們應該做出可讀的句子。所以當我運行我的規範時,我的輸出是這樣的'課程 結束日期不應該在課程開始日期 驗證 應該要求名稱設置 應該要求設置course_code ... '你是什麼這對於閱讀規範的人來說是正確的。我不確定現在採用哪種方法。有什麼想法嗎? – MMinhas 2011-12-19 22:57:15

+0

從我的角度來看,我們應該努力使規格和控制檯輸出可讀。但是,如果你不能同時使用輸出(請參閱爲什麼),那麼如果以詳細模式使用輸出,控制檯的輸出更重要。如果你只使用點,那麼規範更重要。但我絕對想看到一個好的可讀控制檯輸出,特別是當測試失敗時:) – 2013-10-22 15:41:00

3

我想你可以在兩種兩種方式解決這個:

要麼你需要date = DateTime.now.to_date將你帶入到before(:each)塊。

context 'validations' do 
    before(:each) { date = DateTime.now.to_date } 

    it { should allow_value(date).for(:start_date) } 
    it { should_not allow_value(date - 10).for(:start_date) } 
    it { should allow_value(date + 10).for(:end_date)} 
    it { should_not allow_value(date - 10).for(:end_date)} 
end 

或者你可以使用rails日期助手。

context 'validations' do 
    it { should allow_value(Date.today).for(:start_date) } 
    it { should_not allow_value(10.days.ago).for(:start_date) } 
    it { should allow_value(10.days.from_now).for(:end_date)} 
    it { should_not allow_value(10.days.ago).for(:end_date)} 
end