2

I am using such kind of validation in my rails 3.1 project.

validates_presence_of :sales_price
validates_presence_of :retail_price
validates_numericality_of :sales_price, :greater_than => 0,
                          :allow_blank => true
validates_numericality_of :retail_price, :greater_than => 0,
                          :allow_blank => true

validate :sales_price_less_than_retail

def sales_price_less_than_retail
  if sales_price >= retail_price
    errors.add(:sales_price, "must be less than retail price.")
  end
end

I'm testing models using rspec. Everything was ok when i used only rails standard validation helpers. But when i wrote custom validator(sales_price_less_than_retail) tests started to fail.

Here is the code of the test:

it { should validate_presence_of :sales_price }
it { should validate_presence_of :retail_price }
it { should validate_numericality_of :sales_price }
it { should validate_numericality_of :retail_price }

Here is the factory:

Factory.define :offer_option do |f|
  f.sales_price          rand(21) + 10  # $10-$30
  f.retail_price         { |a| a.sales_price * 2 }
end

When i run the test i get such errors:

Failures:

1) OfferOption

Failure/Error: it { should validate_presence_of :sales_price }
 NoMethodError:
   undefined method `>=' for nil:NilClass
 # ./app/models/offer_option.rb:38:in `sales_price_less_than_retail'
 # ./spec/models/offer_option_spec.rb:18:in `block (2 levels) in <top (required)>'

2) OfferOption

 Failure/Error: it { should validate_presence_of :retail_price }
 ArgumentError:
   comparison of BigDecimal with nil failed
 # ./app/models/offer_option.rb:38:in `>='
 # ./app/models/offer_option.rb:38:in `sales_price_less_than_retail'
 # ./spec/models/offer_option_spec.rb:19:in `block (2 levels) in <top (required)>'

I guess everything should be ok because rspec should test validators separately, but it seems that it calls custom validator after calling validates_presence_of in my test. The problem disappears when i remove custom validator.

What am I doing wrong?

1 Answer 1

7

I assume that this is because validate_presence_of rspec helper set offer_option.sales_price = nil and then call valid? on offer_option. When calling valid?, it runs all your validations, so your custom validation as well. And then you get this error, cause there is no '>=' method on nil.

if you change sales_price_less_than_retail to:

def sales_price_less_than_retail
  return if sales_prices.blank? || retail_price.blank?

  if sales_price >= retail_price
    errors.add(:sales_price, "must be less than retail price.")
  end 
end

Then it should works.

Sign up to request clarification or add additional context in comments.

3 Comments

I don't think this would work if the sales_price or retail_price were set to empty strings from a form input. You would need to check if they were present. I will usually return from the method if the conditions aren't met rather than having a long chain of conditionals. return if sales_price.blank? || retail_price.blank?
True, I need to improve my example
Thanks for this! Although this seems like a hack, is there something fundamentaly wrong with how we are writing out test?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.