73

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

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 方法而不是 new 方法来获取错误对象。

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

有人有意见吗?

4

7 回答 7

96

恭喜你努力进入 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

哦,这是一个很好的 railscast,它比我解释得更好:

祝你好运 :)


更新:从3.0 版开始,工厂女孩的语法发生了变化。我已经修改了我的示例代码以反映这一点。

于 2011-09-24T05:22:24.460 回答
44

测试模型验证(以及更多活动记录)的一种更简单的方法是使用类似shoulda显着的 gem 。

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

describe User

  it { should validate_presence_of :name }

end
于 2011-10-01T19:00:03.730 回答
16

试试这个:

it "should require a username" do
  user = User.create(:username => "")
  user.valid?
  user.errors.should have_key(:username)
end
于 2013-07-21T18:29:36.697 回答
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
于 2017-03-31T08:49:56.353 回答
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")

然后,我让我的模型规范测试某些东西是否有效,然后我的功能规范测试错误消息的确切输出。仅供参考,这些功能规格需要 Capybara,可在此处找到。

于 2013-04-03T12:28:51.547 回答
0

就像@nathanvda 所说,我会利用 Thoughtbot 的Shoulda Matchers gem。有了这种摇摆,您可以按以下方式编写测试以测试是否存在,以及任何自定义错误消息。

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
于 2017-10-05T17:22:15.557 回答
0

在这里聚会有点晚了,但如果你不想添加应该匹配器,这应该与 rspec-rails 和 factorybot 一起使用:

# ./spec/factories/user.rb
FactoryBot.define do
  factory :user do
    sequence(:username) { |n| "user_#{n}" }
  end
end

# ./spec/models/user_spec.rb
describe User, type: :model do
  context 'without a username' do
    let(:user) { create :user, username: nil }

    it "should NOT be valid with a username error" do
      expect(user).not_to be_valid
      expect(user.errors).to have_key(:username)
    end
  end
end
于 2018-04-24T18:44:31.643 回答