如何测试关联属性的唯一性

时间:2018-05-22 17:39:15

标签: ruby-on-rails rspec

鉴于用户可以拥有多个地址,我正在尝试验证给定用户只能拥有给定address_type的一个地址。例如,用户可以拥有主地址和帐单地址,但用户不能拥有两个主地址。如何在我的模型上强制执行该规则,以及如何测试它?我目前最好的猜测是我需要验证address_type的唯一性范围为user_id,但此代码阻止两个地址存在相同类型。我见过其他人编写的代码与此非常相似,但检查字符串而不是枚举。

<!-- language: lang-ruby -->
# user.rb
class User < ApplicationRecord
  has_many :addresses
end

# address.rb
class Address < ApplicationRecord
  belongs_to :user
  enum :address_type => { :primary, :mailing, :billing }
  validates :address_type, :uniqueness => { :scope => :user_id }
end

1 个答案:

答案 0 :(得分:1)

Rails唯一性验证与整数列完美匹配。但是,你的枚举定义不是有效的Ruby语法。

class Address < ApplicationRecord
  belongs_to :user
  enum :address_type => [ :primary, :mailing, :billing ]
  # or preferably with Ruby 2.0+ hash syntax
   enum address_type: [ :primary, :mailing, :billing ]
  # ...
end

您可以通过调用模型实例上的.valid?并检查errors object来测试验证:

require 'rails_helper'

RSpec.describe Address, type: :model do
  let(:user) { create(:user) }
  it "should require the user id to be unique" do
    Address.create(user: user, address_type: :primary)
    duplicate = Address.new(user: user, address_type: :primary)
    expect(duplicate.valid?).to be_falsy
    expect(duplicate.errors.full_messages_for(:address_type)).to include "Address type has already been taken"
  end
end

谨防仅测试expect(duplicate.valid?).to be_falsyexpect(duplicate.valid?)因为它可能导致误报/否定。而是测试特定的错误消息或密钥。 Shoulda-matchers对于此目的非常好,但并非绝对必要。

require 'rails_helper'

RSpec.describe Address, type: :model do
  # shoulda-matchers takes care of the boilerplate
  it { should validate_uniqueness_of(:address_type).scoped_to(:user_id) }
end

您还应考虑在addresses.address_typeaddresses.user_id上添加复合唯一索引,因为这会阻止race issues