Testing custom validators with Minitest

守給你的承諾、 提交于 2019-12-23 18:59:00

问题


I have multiple models with email validation. Therefore I've extracted the validation into a custom validator. I dit this by following the tutorial of the Rails Guides.

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

So far, so good. But since I've extracted the functionality of email validation into it's own scope I also want to test it separately. I don't want to add the same email format tests to every model.

I found another question which also asked the same but for RSpec. But since I haven't worked with stubs and mocks yet, I don't know how to port the tests to Minitest tests. I haven't found any resources which test custom validators with Minitest.

Does anybody know how to write such tests for custom validators in Minitest (not using specs!)?


回答1:


What (I think) you are asking for here is testing this validator in isolation. This means that it will be tested once, in an isolated test, which will do exactly what you said:

I don't want to add the same email format tests to every model.

The approach I would take here is to create just a test class in a test file, mix-in the ActiveRecord::Validations module and test the class itself.

# 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 = "valid@email.com"
    assert_nil obj.errors[:email]
    assert obj.valid?
  end
end

I haven't tested the code above, but I think that it should give you an idea/direction.

HTH




回答2:


Here's my implemented solution based on the answer of Ile 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@example.com', 'invalid@ example.com', 'invalid@@example.com', 'invalid', 'invalid@example']

    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

Really cool solution and is also usable for other tests.



来源:https://stackoverflow.com/questions/33976516/testing-custom-validators-with-minitest

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!