如何使用 Shoulda 正确检查唯一性和范围

one*_*one 3 rspec shoulda

我有一个User具有items. 该:name项目的应该是用户唯一的,但它应该允许不同的用户拥有的项目具有相同名称。

Item 模型当前设置为:

class Item < ApplicationRecord
  belongs_to :user
  validates :name, case_sensitive: false, uniqueness: { scope: :user }
end
Run Code Online (Sandbox Code Playgroud)

这适用于验证内部用户,但仍允许其他用户使用相同的名称保存项目。

我如何使用 RSpec/Shoulda 进行测试?

我目前的测试是这样写的:

describe 'validations' do
    it { should validate_uniqueness_of(:name).case_insensitive.scoped_to(:user) }
  end
Run Code Online (Sandbox Code Playgroud)

但是这个测试失败了,因为:

Failure/Error: it { should validate_uniqueness_of(:name).scoped_to(:user).case_insensitive }

       Item did not properly validate that :name is case-insensitively
       unique within the scope of :user.
         After taking the given Item, setting its :name to ‹"an
         arbitrary value"›, and saving it as the existing record, then making a
         new Item and setting its :name to a different value, ‹"AN
         ARBITRARY VALUE"› and its :user to a different value, ‹nil›, the
         matcher expected the new Item to be invalid, but it was valid
         instead.
Run Code Online (Sandbox Code Playgroud)

然而,这是我想要的行为(除了应该nil为用户选择的奇怪部分)。当用户不同时,相同的名称应该是有效的。

可能是我没有正确使用范围测试,或者这对应该是不可能的,这里是范围测试的描述。在这种情况下,您将如何编写模型测试来测试这种行为?

one*_*one 6

解决这个问题的方法有三:

  1. 范围到:user_id而不是:user在模型中

  2. 重新编写模型的验证以包含所有唯一性要求作为散列的一部分

  3. 测试范围 :user_id

问题中的代码将起作用,因为它不区分大小写地正确检查唯一性,但最好将所有唯一性要求包括为散列,因为文档中的示例即使对于单个声明也采用这种形式(而且,它是唯一的我可以找到使应该以正确的行为通过测试的方式)。

这是工作代码的样子:

模型

class Item < ApplicationRecord
  belongs_to :user
  validates :name, uniqueness: { scope: :user_id, case_sensitive: false }
end
Run Code Online (Sandbox Code Playgroud)

测试

RSpec.describe Item, type: :model do
  describe 'validations' do
    it { should validate_uniqueness_of(:name).scoped_to(:user_id).case_insensitive }
  end
end
Run Code Online (Sandbox Code Playgroud)