2011-09-24 142 views
66

我已经在Rails中开始了我的TDD之旅,并且遇到了一个关于模型验证测试的小问题,我似乎找不到解决方案。比方说,我有一个用户模式,Rails 3.1,RSpec:测试模型验证

class User < ActiveRecord::Base 
    validates :username, :presence => true 
end 

和一个简单的测试

it "should require a username" do 
    User.new(:username => "").should_not be_valid 
end 

这正确测试存在验证,但如果我想更具体的是什么?例如,在错误检测full_messages对象..

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

我关于初始尝试(使用should_not be_valid)关注的是,RSpec的不会产生描述性错误消息。它只是说“预计有效?返回假,是真的。”但是,第二个测试示例有一个小缺点:它使用create方法而不是新方法来获取错误对象。

我希望我的测试能够更具体地了解他们正在测试的内容,但同时不必触摸数据库。

任何人有任何输入?

回答

91

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

二,恭喜你用ROR努力进入TDD我保证,一旦你开始你不会回头。

最简单快速和肮脏的解决方法是将产生每个测试的这样的前一个新的有效模式:

before(:each) do 
    @user = User.new 
    @user.username = "a valid username" 
end 

,但我的建议是你办厂所有的模式,将产生为你自动生成有效的模型,然后你可以混合个人属性,看看你的验证。我喜欢用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 

哦雅,这里是a good railscast这可以解释这一切比我更好:

好运:)


UPDATE:作为version 3.0工厂女孩,语法上有改变。我已经修改了我的示例代码以反映这一点。

+2

非常感谢马修。有没有办法接近我试图测试的错误? X.should_not be_valid对我来说似乎很通用,谁知道在路上会有什么事会使记录失效。这个测试然后会在错误的地方失败。顺便说一下,我想我已经将你的答案标记为已接受。我不是吗? – Feech

+7

对,所以这就是我为什么要争取工厂的原因。您编写代码以在一个地方生成一个有效的用户,然后您编写一个测试以确保在所有单个测试之前确保其有效,从而确保您可以使其失效。这样,如果由于某种原因,你改变了你的模型,所以工厂更长的时间产生一个有效的用户'Factory.build(:user).should be_valid'测试将失败,你会知道你必须更新你的工厂...得到它? (是的,你接受了我的答案) – Matthew

+0

完美的解释。再次感谢。 – Feech

41

测试模型验证(以及更多活动记录)的更简单方法是使用像shouldaremarkable这样的宝石。

他们将允许测试如下:功能或要求规格

describe User 

    it { should validate_presence_of :name } 

end 
+1

这是一个好签,你必须在模型中的关联,但要注意,它不会实际尝试没有名称创建一个用户,并检查它的有效性 – brafales

+3

@brafales没有实际上,据我所知这正是早该做的:它将尝试用空白名称创建对象,并且它应该给出错误。 – nathanvda

+2

你是对的,好像我读的代码错误https://github.com/thoughtbot/shoulda-matchers/blob/master/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb – brafales

0

我历来处理错误的内容规范。所以,举例来说,我有一个类似的规范,我将在下面凝结:

特色规格例

before(:each) { visit_order_path } 

scenario 'with invalid (empty) description' , :js => :true do 

    add_empty_task         #this line is defined in my spec_helper 

    expect(page).to have_content("can't be blank") 

那么,我有我的模型规范测试的东西是否是有效的,但后来我功能规范测试错误消息的确切输出。仅供参考,这些功能规格要求水豚可以找到here

15

试试这个:

it "should require a username" do 
    user = User.create(:username => "") 
    user.valid? 
    user.errors.should have_key(:username) 
end 
+0

这是我最喜欢的,很扎实,检查键和不是消息,这是一个细节 – ecoologic

+3

可以只使用用户= User.new(:用户名=>“”) 避免撞到分贝 –

+0

@TaufiqMuhammadi'new'不会碰到分贝级验证,例如唯一性指标约束。 – mnort9

2
在新版本的RSpec

,你应该使用期望而应,否则你会得到警告:

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 
+0

您还应该在示例名称中使用现在时动词而不是应该使用现在时动词。上面的内容可以被重写为“”有一个有效的工厂“'和'”需要一个用户名“'”。 – BrunoFacca

0

像@nathanvda说,我会好好把握Thoughtbot的Shoulda Matchers宝石。随着摇摆,您可以按照以下方式编写测试,以测试是否存在以及任何自定义错误消息。

RSpec.describe User do 

    describe 'User validations' do 
    let(:message) { "I pitty da foo who dont enter a name" } 

    it 'validates presence and message' do 
    is_expected.to validate_presence_of(:name). 
     with_message message 
    end 

    # shorthand syntax: 
    it { is_expected.to validate_presence_of(:name).with_message message } 
    end 

end