我已经在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
这正确地测试了状态验证,但如果我想更具体一点怎么办?例如,在errors对象上测试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方法来获取errors对象。
我希望我的测试更具体地说明他们正在测试什么,但同时不必触摸数据库。
有人有任何意见吗?
答案 0 :(得分: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
哦,你好a good railscast解释这一切比我好:
祝你好运:)更新:从version 3.0开始,工厂女孩的语法已经改变。我修改了我的示例代码以反映这一点。
答案 1 :(得分:42)
测试模型验证(以及更多活动记录)的更简单方法是使用像shoulda或remarkable这样的宝石。
他们将允许进行如下测试:
describe User
it { should validate_presence_of :name }
end
答案 2 :(得分:16)
试试这个:
it "should require a username" do
user = User.create(:username => "")
user.valid?
user.errors.should have_key(:username)
end
答案 3 :(得分:4)
在新版本的rspec中,您应该使用expect,否则您将收到警告:
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
答案 4 :(得分: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可以找到here。
答案 5 :(得分: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
答案 6 :(得分:0)
这里派对有点晚了,但是如果你不想添加shoulda匹配器,这应该适用于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