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

时间:2018-03-17 10:59:01

标签: rspec shoulda

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

项目模型目前设置为:

class Item < ApplicationRecord
  belongs_to :user
  validates :name, case_sensitive: false, uniqueness: { scope: :user }
end

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

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

我目前的测试写成:

describe 'validations' do
    it { should validate_uniqueness_of(:name).case_insensitive.scoped_to(: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这是不可能的,这里是the description of scoped tests。在这种情况下,您将如何编写模型测试来测试此行为?

2 个答案:

答案 0 :(得分:1)

这样做的解决方案有三个方面:

  1. 范围为:user_id而不是模型中的:user

  2. 在模型上重写验证,将所有唯一性要求作为哈希的一部分包含

  3. 将测试范围限定为:user_id
  4. 问题中的代码将起作用,它正确地检查不区分大小写的唯一性,但最好将所有唯一性要求包括为散列,因为the docs中的示例即使对于单个声明也采用此形式(另外,这是我能找到的唯一方法,让Shoulda测试通过正确的行为)。

    这就是工作代码的样子:

    模型

    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
    

答案 1 :(得分:0)

我尝试过enum

模型

  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 }

但总是出现类型错误(即enum值在测试中为大写)

  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