【问题标题】:RSpec fails when using custom validator使用自定义验证器时 RSpec 失败
【发布时间】:2011-11-14 10:45:57
【问题描述】:

我在我的 rails 3.1 项目中使用了这种类型的验证。

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

我正在使用 rspec 测试模型。当我只使用 rails 标准验证助手时,一切都很好。但是当我编写自定义验证器(sales_price_less_than_retail)时,测试开始失败。

这是测试的代码:

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 }

这里是工厂:

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

当我运行测试时,我得到这样的错误:

失败:

1) 报价选项

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) 报价选项

 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)>'

我想一切都应该没问题,因为 rspec 应该单独测试验证器,但它似乎在我的测试中调用 validates_presence_of 之后调用了自定义验证器。 当我删除自定义验证器时,问题就消失了。

我做错了什么?

【问题讨论】:

    标签: ruby-on-rails ruby validation rspec


    【解决方案1】:

    我假设这是因为 validate_presence_of rspec 助手设置 offer_option.sales_price = nil 然后调用有效?在 offer_option 上。调用 valid? 时,它会运行您的所有验证,以及您的自定义验证。然后你得到这个错误,因为 nil 上没有 '>=' 方法。

    如果您将 sales_price_less_than_retail 更改为:

    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
    

    那么它应该可以工作了。

    【讨论】:

    • 如果sales_price 或retail_price 设置为表单输入中的空字符串,我认为这不会起作用。您需要检查它们是否存在。如果条件不满足,我通常会从该方法返回,而不是有一长串条件。 return if sales_price.blank? || retail_price.blank?
    • 没错,我需要改进我的例子
    • 谢谢!虽然这看起来像是一个 hack,但我们编写测试的方式是否存在根本性的问题?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多