Rails 5.2 rspec - Как проверить, действительно ли модель использует пользовательский валидатор? - PullRequest
1 голос
/ 15 мая 2019

Я создал собственный валидатор, у которого есть свои собственные специальные юнит-тесты, чтобы проверить, что он работает.

Используя там-соответствия было предложено добавить validates_with совпадение,так что вы могли бы написать:

subject.validates_with(:custom_validator)

Правильно было отклонено предложение, поскольку оно не проверяет поведение модели.

Но в моей модели есть 4 поля, в которых используется пользовательский валидатори я хочу, чтобы это поведение было проверено, то есть чтобы эти 4 поля были проверены, так же, как я проверяю, что они проверяются на наличие:

describe '#attribute_name' do
  it { is_expected.to validate_presence_of(:attribute_name) }
end

Итак, как я могу написать тест, который в основномделает то же самое, что-то вроде этого:

describe '#attribute_name' do
  it { is_expected.to use_custom_validator_on(:attribute_name) }
end

Этот вопрос задает то же самое, и ответ предлагает построить тестовую модель.Однако моему валидатору требуется опция, она используется следующим образом:

\ app \ models \ fund.rb

class Fund < ActiveRecord
  validates :ein, digits: { exactly: 9 }
end

Так что, если я создаю тестовую модельи протестируйте его, как предложено:

it 'is has correct number of digits' do
  expect(build(:fund, ein: '123456789')).to be_valid
end

it 'is has incorrect number of digits' do
  expect(build(:fund, ein: '123').to be_invalid
end

Я получаю ошибку RecordInvalid (от моего собственного валидатора! lol), говоря, что я не предоставил требуемую опцию для валидатора.Эта опция называется «точно».

1) Fund#ein validates digits
     Failure/Error: raise ActiveRecord::RecordInvalid # option :exactly was not provided (incorrect usage)

     ActiveRecord::RecordInvalid:
       Record invalid

Так что Rspec не «видит» значение «9», определенное в файле модели?

Очевидно, что нет смысла определять это втест как это определенное поведение, которое я пытаюсь проверить.Думайте об этом, как о тестировании validates_length_of для опции { length: x }.

Конечно, должен быть способ проверить, установлен ли этот пользовательский параметр валидатора на модели?

Код валидатора

class DigitsValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    return if value.blank?

    length = options[:exactly]
    regex = /\A(?!0{#{length}})\d{#{length}}\z/
    return unless value.scan(regex).empty?

    record.errors[attribute] << (options[:message] || error_msg(length))
  end

  private

  def error_msg(length)
    I18n.t('activerecord.errors.custom.digits_attr_invalid', length: length) if length
    raise ActiveRecord::RecordInvalid # option :exactly was not provided (incorrect usage)
  end
end

Интересное примечание

Очевидно, что если я уберу строку 'рейза' из DigitsValidator, тогда оба теста пройдут успешно.Что-то не так с моим кодом, который я не вижу?

Ответы [ 2 ]

1 голос
/ 16 мая 2019

Я думаю, вам нужно добавить оператор возврата, нет?: -)

 def error_msg(length)
   return I18n.t('activerecord.errors.custom.digits_attr_invalid', length: length) if length
   raise ActiveRecord::RecordInvalid # option :exactly was not provided (incorrect usage)
 end

В качестве альтернативы, удалите этот метод и используйте защиту после установки length:

  class DigitsValidator < ActiveModel::EachValidator
    def validate_each(record, attribute, value)
      return if value.blank?

      length = options[:exactly]
      raise ActiveRecord::RecordInvalid if length.nil?

      regex = /\A(?!0{#{length}})\d{#{length}}\z/
      return unless value.scan(regex).empty?

      record.errors[attribute] << 
        (options[:message] || 
          I18n.t('activerecord.errors.custom.digits_attr_invalid', length: length))
      end
    end
1 голос
/ 15 мая 2019

Я думаю, вы не должны стремиться к проверке, использует ли модель определенный валидатор. Скорее проверьте, является ли модель действительной / недействительной в определенных случаях. Другими словами, вы должны быть в состоянии проверить поведение модели, не зная реализации.

Таким образом, в этом случае вы должны правильно настроить модель с помощью опции «точно» для валидатора и проверить, достаточно ли проверки модели в целом.

С другой стороны, если вы беспокоитесь о том, что кто-то в будущем будет неправильно использовать валидатор и «именно» является обязательной опцией для валидатора, то вы должны выдавать ошибку каждый раз, когда опция отсутствует, и тестировать Валидатор изолированно, как описано здесь: Как проверить пользовательский валидатор?

...