如何为只验证两个属性之一的模型编写规范(xor)?

时间:2016-05-19 00:02:19

标签: ruby-on-rails ruby-on-rails-4 rspec factory-bot rspec-rails

这是为具有两个替代验证的模型编写模型,工厂和规范的正确方法(两者都没有设置值,两者都可以为零吗?

我是按照以下方式做到的。但是,我不确定这可能是最优雅的方式。

models / invoice.rb

class Invoice < ActiveRecord::Base
  validates :payment_term, presence: true, allow_nil: true
  validates :interest_on_arrears, numericality: true, allow_nil: true
  validate :choose_xor_date
  private
  def choose_xor_date
    unless deadline.blank? ^ payment_term.blank?
      errors.add(:base, 'specify a deadline or a payment term. 
        Not both empty, nor both filled')
    end
  end
end

模型/ invoice_spec.rb

RSpec.describe Invoice, type: :model do
  describe 'validations' do
    it 'fails validation with both deadline and payment_term filled' do 
      invoice_with_deadline = build(:invoice, deadline: '2016-02-20', payment_term: '')
      invoice_with_payment_term = build(:invoice, deadline: '', payment_term: '2')
      invoice_with_deadline_and_payment_term =
        build(:invoice, deadline: '2016-02-20', payment_term: '2')
      expect(invoice_with_deadline).to be_valid
      expect(invoice_with_payment_term).to be_valid
      expect(invoice_with_deadline_and_payment_term).to be_invalid
    end 
  end
end

工厂/ invoice.rb

FactoryGirl.define do
  factory :invoice do
    deadline "2016-02-20"
    payment_term "2"
  end
end

特征/ invoice_feature_spec.rb

# User create with parameter when creating invoice local object. 
describe 'when user has invoice' do
  @invoice = create(:invoice, deadline: '2016-02-20', payment_term: '') 
  visit invoices_path
  click_link I18n.t('button.show')
end

1 个答案:

答案 0 :(得分:0)

你几乎就在那里。以下是我的不同之处:

在模型中,只有几条关于命名的评论:

  • 拖欠&#34;而不是&#34;欠款&#34;。
  • 我认为validate_deadline_xor_payment_termvalidate_deadline_or_payment_term_chosen是验证方法的更好名称。 &#34;选择&#34;实际上是XOR的一个很好的英文翻译。

在型号规格中,

  • 您已经拥有的单一规格应该是三个规格,每个规格一个对象和期望。
  • 如果两个字段均未设置,则需要第四个规范来测试模型是否无效。
  • 您明确正确设置了所有可选值,因此很容易看出它们是什么。如果您按照我的建议更改了工厂,则可以删除空白工厂。

在工厂中,我会删除默认值。如果值是可选的,则具有非限定名称的工厂不应具有可选值。读者很容易记住该约定,并且未指定的值为零是有意义的。如果具有不合格名称的工厂确实具有可选值集,则读者无法知道该值是什么。

功能规范

  • 应位于it块中,而不是describe块中。实际上,由于它是a feature spec,因此在feature块中包围整个要素会更好,并且在此示例中使用scenario
  • 需要预测索引页面上的内容(expect(page).to have_content等)
  • 需要对展会页面上的内容有所期待