Rails 3 自定义验证和应该

发布于 2024-12-22 00:01:12 字数 3940 浏览 1 评论 0原文

这些天我在 Shoulda 和 Rspec 之间摇摆。我已经阅读并玩过相当多的 RSpec,但对 Shoulda 的了解并没有那么多。我发现 Shoulda 的一行断言更容易阅读,并且测试看起来更干净。但是当我不知道如何在 Shoulda 中编写特定断言时,我切换到 RSpec。不过对此并不是很高兴。

这就是我今天所做的。我为我的模型课程编写了一些自定义验证。一门课程有一个start_date和一个end_date。围绕它有一些规则。

  • start_dateend_date 均为强制
  • start_date 不能晚于今天
  • end_date 不能早于 start_date< /code>

我知道有一些安静的宝石可以为我做到这一点。但因为我是新人,所以我认为自己动手并边学边学可能是个好主意。

所以这就是我的模型的样子

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 编写最后一个“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 不为零?

验证在视图中运行良好。我的意思是,根据我输入的数据类型,我会得到正确的验证错误。但我的测试失败了。我已经研究这个有一段时间了,但无法弄清楚我到底做错了什么。

Am swinging between Shoulda and Rspec these days. I have read and played around a fair bit with RSpec but not that much with Shoulda. I find Shoulda's one line assertions easier to read and the test looks cleaner. But when I can't figure out how write a particular assertion in Shoulda I switch to RSpec. Not very happy about it though.

So here is what I did today. I wrote some custom validations for my model Course. A course has a start_date and an end_date. There are a few rules around it.

  • start_date and end_date are both mandatory
  • start_date cannot be later than today
  • end_date cannot be before the start_date

I know there are quiet a few gems out there that could have done it for me. But coz I am new I thought it might be good idea to do it myself and learn as I go.

So this is what my model looks like

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

My custom validations are as follows

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

And following is my 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

Now before I wrote the last "it" block using Rspec I had something like this in my validations context

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

And I got the following failure

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)"]

Am not sure what am I doing wrong here. Is it my custom validation code that is not correct or I need to setup something before I run the last assertion so that start_date is not nil when testing end_date?

The validations work fine in the view. I mean I get the right validation errors depending on the kind of data I input. But am test is failing. I have been looking at this for a while now but cannot figure out what exactly am I doing wrong.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

流心雨 2024-12-29 00:01:13

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

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

I think you could tackle this in one of two two ways:

Either you need to place you date = DateTime.now.to_date into to before(:each) block.

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

Or you could use the rails date helpers.

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
对风讲故事 2024-12-29 00:01:13

@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

@nickgrim already answered the question, but I want to add a comment. The point of describe and it is to encourage sentences that start with the words "describe" and "it". In your example, you've got this:

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

"it end date ...." is not a sentence. Please write that as something like:

it "validates that end date should be >= start date" do
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文