Rails 3 自定义验证和应该
这些天我在 Shoulda 和 Rspec 之间摇摆。我已经阅读并玩过相当多的 RSpec,但对 Shoulda 的了解并没有那么多。我发现 Shoulda 的一行断言更容易阅读,并且测试看起来更干净。但是当我不知道如何在 Shoulda 中编写特定断言时,我切换到 RSpec。不过对此并不是很高兴。
这就是我今天所做的。我为我的模型课程
编写了一些自定义验证。一门课程有一个start_date
和一个end_date。
围绕它有一些规则。
start_date
和end_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
andend_date
are both mandatorystart_date
cannot be later than todayend_date
cannot be before thestart_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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为您可以通过以下两种方式之一解决此问题:
您需要将
date = DateTime.now.to_date
放入before(:each)
块中。或者您可以使用 Rails 日期助手。
I think you could tackle this in one of two two ways:
Either you need to place you
date = DateTime.now.to_date
into tobefore(:each)
block.Or you could use the rails date helpers.
@nickgrim 已经回答了这个问题,但我想添加一条评论。
describe
和it
的目的是鼓励以“describe”和“it”开头的句子。在您的示例中,您得到了这样的内容:“it end date ....”不是一个句子。请写成这样:
@nickgrim already answered the question, but I want to add a comment. The point of
describe
andit
is to encourage sentences that start with the words "describe" and "it". In your example, you've got this:"it end date ...." is not a sentence. Please write that as something like: