【问题标题】:Rails 3 custom validation and shouldaRails 3 自定义验证和应该
【发布时间】:2011-12-19 07:36:05
【问题描述】:

这些天我在 Shoulda 和 Rspec 之间摇摆不定。我已经阅读并玩过 RSpec 的一些内容,但对于 Shoulda 则没有那么多。我发现 Shoulda 的单行断言更易于阅读,并且测试看起来更清晰。但是,当我无法弄清楚如何在 Shoulda 中编写特定的断言时,我会切换到 RSpec。虽然不是很高兴。

这就是我今天所做的。我为我的模型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

以下是我的课程规范

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 编写最后一个“it”块之前,我的验证上下文中有类似的内容

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 不为零?

验证在视图中工作正常。我的意思是,根据我输入的数据类型,我会得到正确的验证错误。但是我的测试失败了。我已经看了一段时间了,但无法弄清楚我到底做错了什么。

【问题讨论】:

    标签: ruby-on-rails validation rspec2 shoulda


    【解决方案1】:

    我认为您可以通过以下两种方式之一解决此问题:

    您需要将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
    

    【讨论】:

      【解决方案2】:

      @nickgrim 已经回答了这个问题,但我想添加评论。 describeit 的重点是鼓励以“描述”和“它”开头的句子。在你的例子中,你有这个:

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

      “it end date ....”不是一个句子。请写成这样:

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

      【讨论】:

      • 感谢您的输入,但我认为当这些步骤在控制台上打印出来时,它们应该是可读的句子。所以当我运行我的规范时,我在控制台上的输出类似于Course end date should not be before course start date validations should require name to be set should require course_code to be set ... 你的建议对于阅读规范的人来说是正确的。我不确定现在要遵循哪种方法。有什么想法吗?
      • 从我的角度来看,我们应该努力使规范和控制台输出可读。但是,如果您不能同时拥有两者(请看为什么),如果您在详细模式下使用输出,那么控制台的输出就更重要了。如果您只使用点,那么规格更重要。但我绝对希望看到一个可读性好的控制台输出,特别是在测试失败时:)
      猜你喜欢
      • 1970-01-01
      • 2011-07-12
      • 1970-01-01
      • 2011-08-01
      • 2011-07-20
      • 1970-01-01
      • 1970-01-01
      • 2011-08-30
      • 1970-01-01
      相关资源
      最近更新 更多