验证前使用模型设置器时出现 shoulda-matchers 错误

shoulda-matches error when using model setters before validarion

我有订单模型,其中包含用于计算用户订购的产品总价的行

before_validation :set_total!
validates :total, presence: true, numericality: { greater_than_or_equal_to: 0 }

set_total 看起来像这样

def set_total!
  self.total = products.map(&price).sum
end

根据我的规范,我正在尝试检查是否实施了总验证 TDD

it { should validate_presence_of(:total) }
it { should validate_numericality_of(:total).is_greater_than_or_equal_to(0) }

不幸的是我收到以下错误

Failure/Error: it { should validate_presence_of(:total) }

   Order did not properly validate that :total cannot be empty/falsy.
     After setting :total to ‹nil› -- which was read back as
     ‹#<BigDecimal:5634b8b81008,'0.0',9(27)>› -- the matcher expected the
     Order to be invalid, but it was valid instead.

     As indicated in the message above, :total seems to be changing certain
     values as they are set, and this could have something to do with why
     this test is failing. If you've overridden the writer method for this
     attribute, then you may need to change it to make this test pass, or
     do something else entirely.

我该如何解决这个问题?

使用 validate_presence_of 匹配器大致相当于手动编写此测试:

describe Order do
  it "fails validation when total is nil" do
    order = Order.new
    order.total = nil
    order.validate
    expect(order.errors[:total]).to include("can't be blank")
    order.total = 42
    expect(order.errors[:total]).not_to include("can't be blank")
  end
end

如果您要 运行 这个测试,您会发现它会失败。为什么?因为在您的模型中,您在执行验证时将 total 设置为非零值。这就是您收到此错误的原因。

所以你真的不需要验证或匹配器,因为两者都不会失败。