我已经开始了我在 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 方法来获取错误对象。
我希望我的测试更具体地说明他们正在测试什么,但同时不必接触数据库。
有人有意见吗?
恭喜你努力进入 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 开始,工厂女孩的语法发生了变化。我已经修改了我的示例代码以反映这一点。
测试模型验证(以及更多活动记录)的一种更简单的方法是使用像 shoulda 或 remarkable 这样的 gem。
他们将允许进行如下测试:
describe User
it { should validate_presence_of :name }
end
尝试这个:
it "should require a username" do
user = User.create(:username => "")
user.valid?
user.errors.should have_key(:username)
end
new
不会达到数据库级别验证,例如唯一性索引约束。
在新版本的 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
"has a valid factory"
和"requires a username"
。
我传统上处理功能或请求规范中的错误内容规范。因此,例如,我有一个类似的规范,我将在下面浓缩:
功能规格示例
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 中找到。
就像@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
在这里聚会有点晚了,但如果你不想添加应该匹配器,这应该与 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
不定期副业成功案例分享
Factory.build(:user).should be_valid
测试将失败,您将知道您必须更新您的工厂...明白了吗? (是的,你接受了 my7 的回答)build
(或者如果您不使用 FactoryGirl,则使用new
)而不是create
。否则会在测试完成之前引发ActiveRecord::RecordInvalid
异常,导致测试失败。