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

Rspec 如何使用Shoulda正确检查唯一性和范围,rspec,shoulda,Rspec,Shoulda,我有一个用户模型,它具有项的子关联。项目的:name对于用户来说应该是唯一的,但是它应该允许不同的用户拥有相同名称的项目 项目模型当前设置为: class Item < ApplicationRecord belongs_to :user validates :name, case_sensitive: false, uniqueness: { scope: :user } end 但该测试失败是因为: Failure/Error: it { should validate_un

我有一个
用户
模型,它具有
项的子关联
。项目的
:name
对于用户来说应该是唯一的,但是它应该允许不同的用户拥有相同名称的项目

项目模型当前设置为:

class Item < ApplicationRecord
  belongs_to :user
  validates :name, case_sensitive: false, uniqueness: { scope: :user }
end
但该测试失败是因为:

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.
然而,这就是我想要的行为(除了应该为用户选择
nil
的奇怪部分)。当用户不同时,相同的名称应有效


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

解决方案有三个方面:

  • 范围为
    :user\u id
    ,而不是模型中的
    :user

  • 在模型上重新编写验证,以将所有唯一性要求作为哈希的一部分包括在内

  • 测试范围为
    :用户id
  • 问题中的代码将在不敏感的情况下正确检查唯一性大小写的情况下工作,但可能最好还是将所有唯一性要求作为哈希包含在内,因为中的示例采用这种形式,即使对于单个声明也是如此(而且,这是我能找到的唯一方法,使Shoulda测试以正确的行为通过)

    这就是工作代码的样子:

    模型


    我用
    enum

    模型

    试验

    但总是得到类型错误(即
    enum
    值在测试中大写)

    但我能够编写一个通过的显式测试

    it 'validates uniqueness of plan_type scoped to benefit_class_id' do
      rp1 = FactoryBot.create(:retirement_plan)
      rp2 = FactoryBot.build(
                             :retirement_plan,
                             benefit_class_id: rp1.benefit_class_id,
                             plan_type: rp1.plan_type,
                             )
      expect(rp2).to_not be_valid
    end
    

    唯一性匹配器不支持将关联用作作用域,有。作为一种解决方法,您应该使用
    scoped\u to(:user\u id)
    。但即使有了这一变化,
    scope\u to
    case\u insensitive
    似乎也不起作用。我认为这是一个错误@一个正常工作的耳机你能报告吗?否则我会做的。@ana06很好,谢谢!我将打开一个新问题,并引用您链接的问题。此处打开的问题:此问题现在似乎已解决。您的第二个测试与shoulda测试的条件不同。如果不区分大小写,shoulda测试将同时查看
    rpp
    rpp
    。您的测试不检查第二种情况,它只是将其设置为相同的值。
    class Item < ApplicationRecord
      belongs_to :user
      validates :name, uniqueness: { scope: :user_id, case_sensitive: false }
    end
    
    RSpec.describe Item, type: :model do
      describe 'validations' do
        it { should validate_uniqueness_of(:name).scoped_to(:user_id).case_insensitive }
      end
    end
    
      validates(:plan_type,
                uniqueness: { scope: :benefit_class_id, case_sensitive: false })
    
          enum plan_type: {
            rrsp: 0,
            dpsp: 1,
            tfsa: 2,
            nrsp: 3,
            rpp: 4,
          }
    
      it { should validate_uniqueness_of(:plan_type).scoped_to(:benefit_class_id).case_insensitive }
    
      1) BenefitClass::RetirementPlan validations should validate that :plan_type is case-insensitively unique within the scope of :benefit_class_id
         Failure/Error:
           is_expected.to validate_uniqueness_of(:plan_type)
             .scoped_to(:benefit_class_id).case_insensitive
    
         ArgumentError:
           'RPP' is not a valid plan_type
    
    it 'validates uniqueness of plan_type scoped to benefit_class_id' do
      rp1 = FactoryBot.create(:retirement_plan)
      rp2 = FactoryBot.build(
                             :retirement_plan,
                             benefit_class_id: rp1.benefit_class_id,
                             plan_type: rp1.plan_type,
                             )
      expect(rp2).to_not be_valid
    end