shoulda-matches error when using model setters before validarion

886 views Asked by At

I have the Order model with the line for calculating total total price of products the user is ordering

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

set_total looks like this

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

On my specs I am trying to check if the total validations are implemented TDD

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

Unfortunately I am receiving the following error

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.

How can I fix this?

1

There are 1 answers

0
Elliot Winkler On BEST ANSWER

Using the validate_presence_of matcher is roughly equivalent to writing this test by hand:

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

If you were to run this test, you would find that this would fail. Why? Because in your model, you set total to a non-nil value when validations are performed. That's why you're getting this error.

So you don't really need the validation or the matcher, since neither one would fail.