Tip nhanh: Không lặp code khi viết Test Validations Model
Bài đăng này đã không được cập nhật trong 8 năm
Tôi đã làm việc trên các ứng dụng và cần viết test cho các model. Tôi đã phải lặp lại các bài test validation cho mỗi trường, mỗi model dẫn đến việc code bị trùng lặp. Vì vậy, tôi sẽ chia sẻ giải pháp cho vấn đề này, sẽ giúp chúng ta tránh lặp lại test cho các validation tương tự nhau trong mỗi model.
Tôi dùng Ruby 2.3.0, Rails 4.2.4. , Minitest và FactoryGirl trong vài viết này. Tôi đã viết một đoạn test cho model Site như sau:
class SiteTest < ActiveSupport::TestCase
  def test_should_require_customer_name
    site = Site.new
    refute site.valid?
    refute site.save
    assert_operator site.errors.count, :>, 0
    assert site.errors.messages.include?(:customer_name)
    assert site.errors.messages[:customer_name].include?("can't be blank")
  end
  def test_should_require_customer_email
    site = Site.new
    refute site.valid?
    refute site.save
    assert_operator site.errors.count, :>, 0
    assert site.errors.messages.include?(:customer_email)
    assert site.errors.messages[:customer_email].include?("can't be blank")
  end
  def test_should_require_host
    site = Site.new
    refute site.valid?
    refute site.save
    assert_operator site.errors.count, :>, 0
    assert site.errors.messages.include?(:host)
    assert site.errors.messages[:host].include?("can't be blank")
  end
  def test_should_require_host_to_be_unique
    theme = FactoryGirl.create(:theme)
    Site.skip_callback(:create, :after, :setup_components)
    existing_site = FactoryGirl.create(:site, theme: theme)
    Site.after_create(:setup_components)
    site = Site.new(host: existing_site.host)
    refute site.valid?
    refute site.save
    assert_operator site.errors.count, :>, 0
    assert site.errors.messages.include?(:host)
    assert site.errors.messages[:host].include?("has already been taken")
  end
  def test_should_require_theme
    site = Site.new
    refute site.valid?
    refute site.save
    assert_operator site.errors.count, :>, 0
    assert site.errors.messages.include?(:theme)
    assert site.errors.messages[:theme].include?("can't be blank")
  end
  def test_should_require_user
    site = Site.new
    refute site.valid?
    refute site.save
    assert_operator site.errors.count, :>, 0
    assert site.errors.messages.include?(:user)
    assert site.errors.messages[:user].include?("can't be blank")
  end
end
Kết quả test:
$ ruby -Ilib:test test/models/site_test.rb
SiteTest
 test_should_require_user PASS (0.45s)
 test_should_require_host PASS (0.01s)
 test_should_require_customer_email PASS (0.01s)
 test_should_require_host_to_be_unique PASS (0.09s)
 test_should_require_theme PASS (0.01s)
 test_should_require_customer_name PASS (0.01s)
Finished in 0.58104s
6 tests, 30 assertions, 0 failures, 0 errors, 0 skips
Quá nhiều code giống nhau cho tính năng test đơn giản. Hãy tưởng tượng nếu bạn phải lặp lại nó trong tất cả các model cho tất cả các trường bạn muốn validation.
Tôi không biết liệu có Gem nào DRY điều này hay không, vì tôi không tìm giải pháp cho vấn đề này. Thay vào đó, tôi bắt đầu sử dụng Ruby để khắc phục vấn đề này. Và đây là những gì tôi đã đưa ra:
module TestModelValidations
  def self.included(klass)
    klass.class_eval do
      def self.test_validates_presence_of(*args)
        args.each do |field_name|
          define_method("test_should_require_#{field_name.to_s}") do
            model = self.class.model_klass.new
            assert_validation(model, field_name, "can't be blank")
          end
        end
      end
      def self.test_validates_uniqueness_of(existing_model, *args)
        args.each do |field_name|
          define_method("test_should_require_#{field_name.to_s}_to_be_unique") do
            params_hash = {}
            params_hash[field_name] = existing_model.send(field_name)
            model = self.class.model_klass.new(params_hash)
            assert_validation(model, field_name, "has already been taken")
          end
        end
      end
    private
      def assert_validation(model, field_name, error_message)
        refute model.valid?
        refute model.save
        assert_operator model.errors.count, :>, 0
        assert model.errors.messages.include?(field_name)
        assert model.errors.messages[field_name].include?(error_message)
      end
    end
  end
  def model_klass
    self.class.name.underscore.split("_test").first.camelize.constantize
  end
end
Bạn có thể đặt file này trong test/support/ và require tất cả file trong thư mục support.Trước khi bắt đầu test thêm dòng này vào file test/test_helper.rb:
Dir[Rails.root.join(‘test’, ‘support’, ‘*.rb’)].each { |f| require f }
Bạn chỉ cần include module này trong mỗi file test để sử dụng phiên bản DRYed up của các bài test validation:
class ActiveSupport::TestCase
 ActiveRecord::Migration.check_pending!
end
Mở class ActiveSupport :: TestCase và extends nó, đây là class mà mỗi class test model kế thừa. Thêm dòng này vào đó:
include TestModelValidations
Bây giờ class trông như sau:
class ActiveSupport::TestCase
 ActiveRecord::Migration.check_pending!
 include TestModelValidations
end
Bây giờ chúng tôi đã sẵn sàng để trình bày phiên bản DRIed up:
class SiteTest < ActiveSupport::TestCase
  test_validates_presence_of :customer_email, :customer_name, :host, :theme, :user
  test_validates_uniqueness_of FactoryGirl.create(:site), :host
end
Vâng, chính là nó  . Đó là tất cả những gì cần để  test model validations. Hãy tưởng tượng việc này được sử dụng này trong tất cả test model của bạn, hy vọng rằng nó sẽ giúp bạn tiết kiệm hàng trăm dòng code và nỗ lực sao chép/dán. Hãy kiểm tra lại để đảm bảo rằng mọi thứ đang hoạt động như trước đây:
. Đó là tất cả những gì cần để  test model validations. Hãy tưởng tượng việc này được sử dụng này trong tất cả test model của bạn, hy vọng rằng nó sẽ giúp bạn tiết kiệm hàng trăm dòng code và nỗ lực sao chép/dán. Hãy kiểm tra lại để đảm bảo rằng mọi thứ đang hoạt động như trước đây:
$ ruby -Ilib:test test/models/site_test.rb
SiteTest
 test_should_require_customer_name PASS (0.34s)
 test_should_require_user PASS (0.01s)
 test_should_require_host PASS (0.01s)
 test_should_require_host_to_be_unique PASS (0.01s)
 test_should_require_theme PASS (0.01s)
 test_should_require_customer_email PASS (0.01s)
Finished in 0.39483s
6 tests, 30 assertions, 0 failures, 0 errors, 0 skips
Boom! Tất cả mọi thứ hoạt động như mong đợi, nhưng là code sạch và thuận tiện hơn. Điều này làm cho việc test validation cho model dễ dàng hơn.
test_validates_uniqueness_of FactoryGirl.create(:site), :host
Lệnh validates này xác nhận tính duy nhất của một trường trong model. Đối số đầu tiên là trường hợp test. Sau đối số đầu tiên, là trường của model bạn muón xác định tính duy nhất của trương hợp đó.
Mong rằng sẽ nhận được nhận xét và câu hỏi từ bạn. Tôi thực sự hy vọng điều này sẽ giúp ích cho bạn!
Thank you!
Tài liệu dịch: https://www.sitepoint.com/quick-tip-dry-up-your-model-validations-tests/
All rights reserved
 
  
 