Showing posts with label rails4. Show all posts
Showing posts with label rails4. Show all posts

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

Monday, October 14, 2013

Testing Responsive web design with Rspec

Recently I am working on an application where we have used zurb foundation for responsive web design. We have separated all devices screen broadly into three categories as follows,

1. small : Screen width upto 590.
2. medium : Screen width upto 1025.
3. large : Screen width uptp 1280.

Very soon we will add new category,
4. xlarge : above 1280+ ;)

Now all things are fine, but until you didn't write acceptance test, you can't make sure that your design is working fine for all screen.

Before taking responsive web design into consideration, we have already written feature specs using rspec + capybara (selenium).

So here we want handy configuration which will not affect existing feature spec and should treat existing specs meant to be written for large screen.

So here is, how I have added configuration for my rspec suite to target testing responsive design.


With above configuration, if you write any feature spec without :device_size then it will run that spec against 'large' screen. And if you want to write a spec for 'small' and 'medium' devices you can write by using metadata 'device_size => :small' or 'device_size => :medium'
e.g.
  feature "XYZ" do
    scenario "abc", :js => true do
      # spec with default screen size i.e large
    end 

    scenario "abc", :js => true, :device_size => :small do
      # spec with small screen size
    end 
  end
With the help of 'config.include ScreenSize, :type => :feature' you can directly change screen size into example.
e.g.
  feature "XYZ" do
    scenario "abc", :js => true do
      set_screen_size(:medium)
      # spec with default screen size i.e large
    end 
  end
If you want to some special configuration based on device size you can do that using,
  config.before(:each, :device_size => :small) do
    # special configuration .........
  end
TODO :
I want to split a test suite into feature/small/*.rb for small devices, feature/large/*.rb for large devices and so on.. and apply specific metadata configuration based on type of suite.

References :
http://www.blaulabs.de/2011/11/22/acceptance-testing-with-responsive-layouts