Friday, May 6, 2016

How to test model validations with validation type

While reading a code or while doing code review, I have see many times people write a test cases for model validation like this,
Before
# app/models/post.rb
class Post < ActiveRecord::Base
  validates :title, presence: true
end
# test/models/post_test.rb
test "should have the necessary required validators" do
  post = Post.new

  assert_not post.valid?
  assert post.errors.has_key(:title)
  # or some times
  assert_equal ["can't be blank"], post.errors.messages[:title]
end
And if same attribute has more than one validation then,
# app/models/post.rb
class Post < ActiveRecord::Base
  validates :title, presence: true, length: { in: 10..60 }
end
Most of the people do,
test "should have the necessary required validators" do
  post = Post.new

  assert_not post.valid?
  assert_equal ["can't be blank", "is too short (minimum is 10 characters)"], post.errors.messages[:title]
end
I think instead we can use symbol for it. Rails internally uses I18n locale support for error messages. For more details have a quick look on reference links provided below.

Means we can take a benifits from it. So improved test case code is,
After
test "should have the necessary required validators" do
  post = Post.new
  assert_not post.valid?
  
  assert post.errors.added? :title, :blank
  assert post.errors.added? :title, :too_short, { count: 20 }
end
While for custom validate method, you can use same approch by defining new key. Key can be any valid symbol.
Before
class Post < ActiveRecord::Base
  has_many :tags

  validates :title, presence: true
  validate :validate_tags_count
  
  private
    def validate_tags_count
      errors.add(:tags, "required alteast 2 tags") if tags.reject(&:marked_for_destruction?).count < 2
    end
end
After
class Post < ActiveRecord::Base
  has_many :tags

  validates :title, presence: true
  validate :validate_tags_count
  
  private
    def validate_tags_count
      errors.add(:tags, :required, count: 2) if tags.reject(&:marked_for_destruction?).count < 2
    end
end
test "should have the necessary required validators" do
  post = Post.new

  assert_not post.valid?
  assert post.errors.added? :tags, :required, { count: 2 }
end
#config/locales/en.yml
# Globally for all models
en:
  errors:
    messages: 
      required: "minimum %{count} tags required" 

or
 
#config/locales/en.yml
# only to post models
en:
  activerecord:
    errors:
      models:
        post:
          attributes:
            tags:
              required: "minimum %{count} tags required"
Same concept will work on rails 5 as well. Tested with rails 5.0.0.beta4.

References :
http://api.rubyonrails.org/classes/ActiveModel/Errors.html
activemodel/lib/active_model/validations/presence.rb
activemodel/lib/active_model/locale/en.yml

2 comments:

  1. Great post. I was checking constantly this blog and I
    am impressed! Extremely helpful info particularly the last part :) I care for such info
    a lot. I was looking for this certain info for
    a long time. Thank you and good luck.

    ReplyDelete
  2. Best No Deposit Bonus Codes in India - Herzamanindir.com
    5 steps1.Visit the official website of No Deposit India.
    Benefits of septcasino using a no deposit 메이저 토토 사이트 bonus.
    Benefits of using a no herzamanindir deposit bonus.
    Benefits kadangpintar of febcasino.com using a no deposit bonus.
    Online Sincere Accessory domain www.online-bookmakers.info

    ReplyDelete