Rails 3.1,RSpec:测试模型验证

Fee*_*ech 70 rspec ruby-on-rails rspec2 rspec-rails ruby-on-rails-3

我已经在Rails中使用TDD开始了我的旅程,并且遇到了一个关于模型验证测试的小问题,我似乎无法找到解决方案.假设我有一个用户模型,

class User < ActiveRecord::Base
  validates :username, :presence => true
end
Run Code Online (Sandbox Code Playgroud)

和一个简单的测试

it "should require a username" do
  User.new(:username => "").should_not be_valid
end
Run Code Online (Sandbox Code Playgroud)

这正确地测试了状态验证,但如果我想更具体一点怎么办?例如,在errors对象上测试full_messages.

it "should require a username" do
  user = User.create(:username => "")
  user.errors[:username].should ~= /can't be blank/
end
Run Code Online (Sandbox Code Playgroud)

我对初始尝试的关注(使用should_not be_valid)是RSpec不会产生描述性错误消息.它只是说"预期有效?返回虚假,变为现实".但是,第二个测试示例有一个小缺点:它使用create方法而不是new方法来获取errors对象.

我希望我的测试更具体地说明他们正在测试什么,但同时不必触摸数据库.

有人有任何意见吗?

Mat*_*hew 96

首先,我想说你有一个邪恶的名字.

第二,祝贺你在ROR上努力进入TDD我保证一旦你开始,你就不会回头了.

最简单快速和肮脏的解决方案是在每个测试之前生成一个新的有效模型,如下所示:

 before(:each) do
    @user = User.new
    @user.username = "a valid username"
 end
Run Code Online (Sandbox Code Playgroud)

但我建议您为所有模型设置工厂,自动为您生成有效模型,然后您可以混淆各个属性并查看是否验证.我喜欢使用FactoryGirl:

基本上一旦你设置好你的测试就会看起来像这样:

it "should have valid factory" do
    FactoryGirl.build(:user).should be_valid
end

it "should require a username" do
    FactoryGirl.build(:user, :username => "").should_not be_valid
end
Run Code Online (Sandbox Code Playgroud)

哦,你好,这是一个很好的轨道广播,解释它比我更好:

祝好运 :)


更新:从版本3.0开始,工厂女孩的语法已更改.我修改了我的示例代码以反映这一点.

  • 是的,所以这就是我为工厂辩护的原因.您编写代码以在一个位置生成有效用户一次,然后编写测试以确保其在所有单个测试之前有效,以确保您可以使其无效.这样,如果由于某种原因你改变你的模型,使工厂更长时间产生一个有效的用户,`Factory.build(:user).should be_valid`测试将失败,你将知道你必须更新你的工厂...得到它?(是的,你接受了我的回答) (7认同)
  • 非常感谢马修.有没有办法更接近我试图测试的错误?X.should_not be_valid对我来说似乎如此通用,谁知道路上的其他东西是否会使记录无效.然后,此测试将在错误的位置失败.顺便说一句,我想我已经接受了你的答案.不是吗? (2认同)

nat*_*vda 43

测试模型验证(以及更多的活动记录)的一种更简单的方法是使用像shoulda非凡的宝石.

他们将允许进行如下测试:

describe User

  it { should validate_presence_of :name }

end
Run Code Online (Sandbox Code Playgroud)

  • @brafales实际上没有,afaik正是应该做的:它会尝试创建一个空白名称的对象,它应该给出一个错误. (3认同)
  • 你是对的,好像我读错了代码https://github.com/thoughtbot/shoulda-matchers/blob/master/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb (2认同)

Win*_*zan 16

试试这个:

it "should require a username" do
  user = User.create(:username => "")
  user.valid?
  user.errors.should have_key(:username)
end
Run Code Online (Sandbox Code Playgroud)

  • 你可以使用user = User.new(:username =>"")来避免命中db (4认同)

day*_*odo 5

在新版本的rspec中,你应该使用expect而不是should,否则你会收到警告:

it "should have valid factory" do
    expect(FactoryGirl.build(:user)).to be_valid
end

it "should require a username" do
    expect(FactoryGirl.build(:user, :username => "")).not_to be_valid
end
Run Code Online (Sandbox Code Playgroud)