3

У меня есть несколько моделей с проверкой подлинности электронной почты. Поэтому я извлек подтверждение в пользовательский валидатор. Я делаю это, следуя руководству Rails Guides.Тестирование пользовательских валидаторов с помощью Minitest

class EmailValidator < ActiveModel::EachValidator 
    def validate_each(record, attribute, value) 
    unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i 
     record.errors[attribute] << (options[:message] || "is not an email") 
    end 
    end 
end 

Пока что так хорошо. Но поскольку я извлек функциональность проверки подлинности электронной почты в свою собственную область, я также хочу протестировать ее отдельно. Я не хочу добавлять те же тесты формата электронной почты для каждой модели.

Я нашел еще один question, который также спросил то же самое, но для RSpec. Но поскольку я еще не работал с заглушками и макетами, я не знаю, как переносить тесты на тесты Minitest. Я не нашел никаких ресурсов, которые проверяют пользовательские валидаторы с помощью Minitest.

Кто-нибудь знает, как написать такие тесты для пользовательских валидаторов в Minitest (не используя спецификации!)?

ответ

3

Что (я думаю), вы просите здесь, тестирует этот валидатор в изоляции. Это означает, что он будет проверен один раз в отдельном тесте, который будет делать именно то, что вы сказали:

Я не хочу добавлять те же тесты формата электронной почты для каждой модели.

Подход, который я хотел бы здесь сделать, - создать только тестовый класс в тестовом файле, микшировать модуль ActiveRecord::Validations и протестировать сам класс.

# test_file.rb 
require 'test_helper' 

class EmailValidatable 
    include ActiveModel::Validations 
    validates_with EmailValidator 
    attr_accessor :email 
end 

class EmailValidatorTest < Minitest::Test 
    def test_invalidates_object_for_invalid_email 
    obj = EmailValidatable.new 
    obj.email = "invalidemail" 
    refute obj.valid? 
    end 

    def test_adds_error_for_invalid_email 
    obj = EmailValidatable.new 
    obj.email = "invalidemail" 
    refute_nil obj.errors[:email] 
    end 

    def test_adds_no_errors_for_valid_email 
    obj = EmailValidatable.new 
    obj.email = "[email protected]" 
    assert_nil obj.errors[:email] 
    assert obj.valid? 
    end 
end 

Я не проверял код выше, но я думаю, что он должен дать вам идею/направление.

НТН

+0

Это именно то, что я искал. Я никогда не думал о создании нового класса и тестировал его оттуда. Я всегда искал способ непосредственного тестирования. Но это выглядит еще лучше! Благодаря! – Tobias

+0

Отлично, я рад, что помог. Не могли бы вы поместить (bin) полный тест? Спасибо :) –

+0

Привет @Ile! Я добавил свое реализованное решение в качестве ответа. Еще раз спасибо! – Tobias

0

Вот мое реализованное решение на основе ответа Иле Eftimov:

require 'test_helper' 

class EmailValidatable 
    include ActiveModel::Validations 
    attr_accessor :email 

    validates :email, email: true 
end 

class EmailValidatorTest < ActiveSupport::TestCase 
    invalid_email_addresses = ['invalid [email protected]', '[email protected] example.com', '[email protected]@example.com', 'invalid', '[email protected]'] 

    def obj; @obj ||= EmailValidatable.new; end 

    test 'should invalidate email address' do 
     invalid_email_addresses.each do |email| 
      obj.email = email 
      assert_not obj.valid? 
     end 
    end 

    test 'should add error for invalid email' do 
     invalid_email_addresses.each do |email| 
      obj.email = email 
      obj.valid? 
      assert_equal I18n.t('errors.messages.not_an_email_address'), obj.errors[:email][0], "no error message for invalid email address \"#{email}\"" 
     end 
    end 

    test 'should validate email address' do 
     obj.email = FFaker::Internet.email 
     assert obj.valid? 
    end 

    test 'should add no error for valid email' do 
     obj.email = FFaker::Internet.email 
     assert obj.errors[:email].blank? 
    end 
end 

Действительно прохладный раствор, а также использоваться для других испытаний.