Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I test :inclusion validation in Rails using RSpec

I have the following validation in my ActiveRecord.

validates :active, :inclusion => {:in => ['Y', 'N']}

I am using the following to test my model validations.

should_not allow_value('A').for(:active)
should allow_value('Y').for(:active)
should allow_value('N').for(:active)

Is there a cleaner and more through way of testing this? I am currently using RSpec2 and shoulda matchers.

EDIT

After some looking around I only found, this probably an 'ok' way of testing this, shoulda does not provide anything for this and anyone who requires it can write their own custom matcher for it.(And probably contribute it back to the project). Some links to discussions that might be intresting:

  • Links which indicate to the above . Link 1 , Link 2

  • should_ensure_value_in_range This one comes close to what can be used, but only accepts ranges and not a list of values. Custom matcher can be based on this.

like image 747
jake Avatar asked Sep 14 '11 11:09

jake


3 Answers

Use shoulda_matchers

In recent versions of shoulda-matchers (at least as of v2.7.0), you can do:

expect(subject).to validate_inclusion_of(:active).in_array(%w[Y N])

This tests that the array of acceptable values in the validation exactly matches this spec.

In earlier versions, >= v1.4 , shoulda_matchers supports this syntax:

it {should ensure_inclusion_of(:active).in_array(%w[Y N]) }
like image 156
Nathan Long Avatar answered Sep 22 '22 05:09

Nathan Long


If you have more elements to test than a boolean Y/N then you could also try.

it "should allow valid values" do
  %w(item1 item2 item3 item4).each do |v|
    should allow_value(v).for(:field)
  end
end
it { should_not allow_value("other").for(:role) }

You can also replace the %w() with a constant you have defined in your model so that it tests that only the constant values are allowed.

CONSTANT = %w[item1 item2 item3 item4]
validates :field, :inclusion => CONSTANT

Then the test:

it "should allow valid values" do
  Model::CONSTANT.each do |v|
    should allow_value(v).for(:field)
  end
end
like image 25
nmott Avatar answered Sep 22 '22 05:09

nmott


I found one custom shoulda matcher (in one of the projects I was working on) which attempts to coming close to test something like this:

Examples:

it { should validate_inclusion_check_constraint_on :status, :allowed_values => %w(Open Resolved Closed) }
it { should validate_inclusion_check_constraint_on :age, :allowed_values => 0..100 }

The matcher tries to ensure that there is a DB constraint which blows up when it tries to save it.I will attempt to give the essence of the idea. The matches? implementation does something like:

  begin
    @allowed_values.each do |value|
      @subject.send("#{@attribute}=", value)
      @subject.save(:validate => false)
    end
  rescue ::ActiveRecord::StatementInvalid => e
    # Returns false if the exception message contains a string matching the error throw by SQL db
  end

I guess if we slightly change the above to say @subject.save and let Rails validation blow up, we can return false when the exception string contains something which close matches the real exception error message.

I know this is far from perfect to contributed back to the project, but I guess might not be a bad idea to add into your project as a custom matcher if you really want to test a lot of the :inclusion validation.

like image 1
jake Avatar answered Sep 23 '22 05:09

jake