【发布时间】:2014-01-29 23:04:55
【问题描述】:
我有一个具有这些验证的模型:
class Tool < ActiveRecord::Base
has_many :repairs
has_many :services
belongs_to :category
belongs_to :location
accepts_nested_attributes_for :repairs
accepts_nested_attributes_for :services
validates :name, :serial, :model, :presence => true
validates_uniqueness_of :serial
end
我的工厂是这样的:
require 'faker'
FactoryGirl.define do
factory :tool do
association :location
association :category
name {Faker::Name.first_name}
model {Faker::Company.name}
serial {Faker::Address.latitude}
end
end
我有一些简单的测试,比如:
it 'is valid with a name, serial, model, location, category' do
expect(build(:tool, location: Location.new(name: 'Sta 72'),
category: Category.new(name: 'Chainsaw'))).to be_valid
end
it 'is invalid without a name' do
expect(build(:tool, name: nil)).to have(1).errors_on(:name)
end
it 'is invalid without a serial' do
expect(build(:tool, serial: nil)).to have(1).errors_on(:serial)
end
it 'is invalid without a model' do
expect(build(:tool, model: nil)).to have(1).errors_on(:model)
end
it 'is invalid with a duplicate serial' do
tool1 = create(:tool, serial: '12345')
tool2 = build(:tool, serial: tool1.serial)
expect(tool2).to have(1).errors_on(:serial)
end
除了最后一次检查以确保:serial 是唯一的测试之外,一切都运行良好。它失败了:
ActiveRecord::RecordInvalid: Validation failed: Name has already been taken
./spec/models/tool_spec.rb:27:in `block (3 levels) in <top (required)>'
当我不验证 Name 的唯一性时,我不知道为什么它会抱怨 Name。第 27 行指的是tool2 = build(:tool, serial: tool1.serial)
编辑*
schema.rb 看起来像这样:
...
create_table "tools", force: true do |t|
t.string "name"
t.string "serial"
t.date "purchased"
t.date "put_in_service"
t.decimal "cost"
t.decimal "value"
t.boolean "in_service"
t.date "retired"
t.datetime "created_at"
t.datetime "updated_at"
t.text "note"
t.integer "condition"
t.text "old_location"
t.string "model"
t.boolean "loaner", default: false
t.integer "location_id"
t.integer "category_id"
end
add_index "tools", ["category_id"], name: "index_tools_on_category_id", using: :btree
add_index "tools", ["location_id"], name: "index_tools_on_location_id", using: :btree
当我只运行失败的测试时,我的测试日志如下所示:
ActiveRecord::SchemaMigration Load (0.6ms) SELECT "schema_migrations".* FROM "schema_migrations"
(0.4ms) BEGIN
(0.3ms) SAVEPOINT active_record_1
SQL (9.3ms) INSERT INTO "locations" ("created_at", "name", "type", "updated_at", "vehicle") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", Mon, 27 Jan 2014 22:28:52 PST -08:00], ["name", "Loaners"], ["type", "Station"], ["updated_at", Mon, 27 Jan 2014 22:28:52 PST -08:00], ["vehicle", true]]
(0.3ms) RELEASE SAVEPOINT active_record_1
(0.3ms) SAVEPOINT active_record_1
Category Exists (1.5ms) SELECT 1 AS one FROM "categories" WHERE "categories"."name" = 'Chainsaws' LIMIT 1
SQL (1.2ms) INSERT INTO "categories" ("created_at", "name", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["created_at", Mon, 27 Jan 2014 22:28:52 PST -08:00], ["name", "Chainsaws"], ["updated_at", Mon, 27 Jan 2014 22:28:52 PST -08:00]]
(0.2ms) RELEASE SAVEPOINT active_record_1
(0.4ms) SAVEPOINT active_record_1
Tool Exists (1.0ms) SELECT 1 AS one FROM "tools" WHERE "tools"."serial" = '12345' LIMIT 1
SQL (1.4ms) INSERT INTO "tools" ("category_id", "created_at", "location_id", "model", "name", "serial", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["category_id", 77], ["created_at", Mon, 27 Jan 2014 22:28:52 PST -08:00], ["location_id", 43], ["model", "Stark LLC"], ["name", "Cristina"], ["serial", "12345"], ["updated_at", Mon, 27 Jan 2014 22:28:52 PST -08:00]]
(0.2ms) RELEASE SAVEPOINT active_record_1
(0.3ms) SAVEPOINT active_record_1
SQL (0.9ms) INSERT INTO "locations" ("created_at", "name", "type", "updated_at", "vehicle") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["created_at", Mon, 27 Jan 2014 22:28:52 PST -08:00], ["name", "Loaners"], ["type", "Station"], ["updated_at", Mon, 27 Jan 2014 22:28:52 PST -08:00], ["vehicle", true]]
(0.3ms) RELEASE SAVEPOINT active_record_1
(0.3ms) SAVEPOINT active_record_1
Category Exists (0.5ms) SELECT 1 AS one FROM "categories" WHERE "categories"."name" = 'Chainsaws' LIMIT 1
(0.3ms) ROLLBACK TO SAVEPOINT active_record_1
(0.6ms) ROLLBACK
编辑->
感谢下面的答案,我的问题得到了解决。我根据他们的回答使用的代码是:
it 'is invalid with a duplicate serial' do
chainsaw = create(:category, name: 'Chainsaw')
tool1 = create(:tool, serial: '12345', category: chainsaw)
tool2 = build(:tool, serial: '12345', category: chainsaw)
expect(tool2).to have(1).errors_on(:serial)
end
我不明白为什么 FactoryGirl 和 Faker 不会创建这两个工具并尊重 validates uniqueness 模型上的 validates uniqueness 约束。它不应该只是重用它刚刚为第一个工具创建的Category(而不是尝试创建一个具有相同名称的新工具),或者为与以前不同的新工具创建一个新的Category ?
啊哈!这就是为什么:
FactoryGirl.define do
factory :category do
name 'Chainsaws'
end
end
我会将这个工厂设置为使用 Faker,它应该可以按我的预期工作。
【问题讨论】:
-
你的架构是什么样的?
-
你的
location或category模型怎么样?它们实际上将由 FactoryGirl.build 创建。其中任何一个都具有name唯一性吗?
标签: ruby-on-rails rspec factory-bot