module Shoulda::Matchers::ActiveModel
This module provides matchers that are used to test behavior within ActiveModel
or ActiveRecord
classes.
### Testing conditional validations
If your model defines a validation conditionally – meaning that the validation is declared with an ‘:if` or `:unless` option – how do you test it? You might expect the validation matchers here to have corresponding `if` or `unless` qualifiers, but this isn’t what you use. Instead, before using the matcher in question, you place the record you’re testing in a state such that the validation you’re also testing will be run. A common way to do this is to make a new ‘context` and override the subject to populate the record accordingly. You’ll also want to make sure to test that the validation is not run when the conditional fails.
Here’s an example to illustrate what we mean:
class User include ActiveModel::Model attr_accessor :role, :admin validates_presence_of :role, if: :admin end # RSpec RSpec.describe User, type: :model do context "when an admin" do subject { User.new(admin: true) } it { should validate_presence_of(:role) } end context "when not an admin" do subject { User.new(admin: false) } it { should_not validate_presence_of(:role) } end end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase context "when an admin" do subject { User.new(admin: true) } should validate_presence_of(:role) end context "when not an admin" do subject { User.new(admin: false) } should_not validate_presence_of(:role) end end
Public Instance Methods
Source
# File lib/shoulda/matchers/active_model/allow_value_matcher.rb, line 296 def allow_value(*values) if values.empty? raise ArgumentError, 'need at least one argument' else AllowValueMatcher.new(*values) end end
The ‘allow_value` matcher (or its alias, `allow_values`) is used to ensure that an attribute is valid or invalid if set to one or more values.
Take this model for example:
class UserProfile include ActiveModel::Model attr_accessor :website_url validates_format_of :website_url, with: URI.regexp end
You can use ‘allow_value` to test one value at a time:
# RSpec RSpec.describe UserProfile, type: :model do it { should allow_value('https://foo.com').for(:website_url) } it { should allow_value('https://bar.com').for(:website_url) } end # Minitest (Shoulda) class UserProfileTest < ActiveSupport::TestCase should allow_value('https://foo.com').for(:website_url) should allow_value('https://bar.com').for(:website_url) end
You can also test multiple values in one go, if you like. In the positive sense, this makes an assertion that none of the values cause the record to be invalid. In the negative sense, this makes an assertion that none of the values cause the record to be valid:
# RSpec RSpec.describe UserProfile, type: :model do it do should allow_values('https://foo.com', 'https://bar.com'). for(:website_url) end it do should_not allow_values('foo', 'buz'). for(:website_url) end end # Minitest (Shoulda) class UserProfileTest < ActiveSupport::TestCase should allow_values('https://foo.com', 'https://bar.com/baz'). for(:website_url) should_not allow_values('foo', 'buz'). for(:website_url) end
#### Caveats
When using ‘allow_value` or any matchers that depend on it, you may encounter an AttributeChangedValueError. This exception is raised if the matcher, in attempting to set a value on the attribute, detects that the value set is different from the value that the attribute returns upon reading it back.
This usually happens if the writer method (‘foo=`, `bar=`, etc.) for that attribute has custom logic to ignore certain incoming values or change them in any way. Here are three examples we’ve seen:
-
You’re attempting to assert that an attribute should not allow nil, yet the attribute’s writer method contains a conditional to do nothing if the attribute is set to nil:
class Foo include ActiveModel::Model attr_reader :bar def bar=(value) return if value.nil? @bar = value end end RSpec.describe Foo, type: :model do it do foo = Foo.new foo.bar = "baz" # This will raise an AttributeChangedValueError since `foo.bar` is now "123" expect(foo).not_to allow_value(nil).for(:bar) end end
-
You’re attempting to assert that a numeric attribute should not allow a string that contains non-numeric characters, yet the writer method for that attribute strips out non-numeric characters:
class Foo include ActiveModel::Model attr_reader :bar def bar=(value) @bar = value.gsub(/\D+/, '') end end RSpec.describe Foo, type: :model do it do foo = Foo.new # This will raise an AttributeChangedValueError since `foo.bar` is now "123" expect(foo).not_to allow_value("abc123").for(:bar) end end
-
You’re passing a value to ‘allow_value` that the model typecasts into another value:
RSpec.describe Foo, type: :model do # Assume that `attr` is a string # This will raise an AttributeChangedValueError since `attr` typecasts `[]` to `"[]"` it { should_not allow_value([]).for(:attr) } end
Fortunately, if you understand why this is happening, and wish to get around this exception, it is possible to do so. You can use the ‘ignoring_interference_by_writer` qualifier like so:
it do should_not allow_value([]). for(:attr). ignoring_interference_by_writer end
Please note, however, that this qualifier won’t magically cause your test to pass. It may just so happen that the final value that ends up being set causes the model to fail validation. In that case, you’ll have to figure out what to do. You may need to write your own test, or perhaps even remove your test altogether.
#### Qualifiers
##### on
Use ‘on` if your validation applies only under a certain context.
class UserProfile include ActiveModel::Model attr_accessor :birthday_as_string validates_format_of :birthday_as_string, with: /^(\d+)-(\d+)-(\d+)$/, on: :create end # RSpec RSpec.describe UserProfile, type: :model do it do should allow_value('2013-01-01'). for(:birthday_as_string). on(:create) end end # Minitest (Shoulda) class UserProfileTest < ActiveSupport::TestCase should allow_value('2013-01-01'). for(:birthday_as_string). on(:create) end
##### with_message
Use ‘with_message` if you are using a custom validation message.
class UserProfile include ActiveModel::Model attr_accessor :state validates_format_of :state, with: /^(open|closed)$/, message: 'State must be open or closed' end # RSpec RSpec.describe UserProfile, type: :model do it do should allow_value('open', 'closed'). for(:state). with_message('State must be open or closed') end end # Minitest (Shoulda) class UserProfileTest < ActiveSupport::TestCase should allow_value('open', 'closed'). for(:state). with_message('State must be open or closed') end
Use ‘with_message` with a regexp to perform a partial match:
class UserProfile include ActiveModel::Model attr_accessor :state validates_format_of :state, with: /^(open|closed)$/, message: 'State must be open or closed' end # RSpec RSpec.describe UserProfile, type: :model do it do should allow_value('open', 'closed'). for(:state). with_message(/open or closed/) end end # Minitest (Shoulda) class UserProfileTest < ActiveSupport::TestCase should allow_value('open', 'closed'). for(:state). with_message(/open or closed/) end
Use ‘with_message` with the `:against` option if the attribute the validation message is stored under is different from the attribute being validated:
class UserProfile include ActiveModel::Model attr_accessor :sports_team validate :sports_team_must_be_valid private def sports_team_must_be_valid if sports_team !~ /^(Broncos|Titans)$/i self.errors.add :chosen_sports_team, 'Must be either a Broncos fan or a Titans fan' end end end # RSpec RSpec.describe UserProfile, type: :model do it do should allow_value('Broncos', 'Titans'). for(:sports_team). with_message('Must be either a Broncos or Titans fan', against: :chosen_sports_team ) end end # Minitest (Shoulda) class UserProfileTest < ActiveSupport::TestCase should allow_value('Broncos', 'Titans'). for(:sports_team). with_message('Must be either a Broncos or Titans fan', against: :chosen_sports_team ) end
##### ignoring_interference_by_writer
Use ‘ignoring_interference_by_writer` to bypass an AttributeChangedValueError that you have encountered. Please read the Caveats section above for more information.
class Address < ActiveRecord::Base # Address has a zip_code field which is a string end # RSpec RSpec.describe Address, type: :model do it do should_not allow_value([]). for(:zip_code). ignoring_interference_by_writer end end # Minitest (Shoulda) class AddressTest < ActiveSupport::TestCase should_not allow_value([]). for(:zip_code). ignoring_interference_by_writer end
@return [AllowValueMatcher]
Source
# File lib/shoulda/matchers/active_model/have_secure_password_matcher.rb, line 33 def have_secure_password(attr = :password) HaveSecurePasswordMatcher.new(attr) end
The ‘have_secure_password` matcher tests usage of the `has_secure_password` macro.
#### Example
class User include ActiveModel::Model include ActiveModel::SecurePassword attr_accessor :password attr_accessor :reset_password has_secure_password has_secure_password :reset_password end # RSpec RSpec.describe User, type: :model do it { should have_secure_password } it { should have_secure_password(:reset_password) } end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should have_secure_password should have_secure_password(:reset_password) end
@return [HaveSecurePasswordMatcher]
Source
# File lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb, line 75 def validate_absence_of(attr) ValidateAbsenceOfMatcher.new(attr) end
The ‘validate_absence_of` matcher tests the usage of the `validates_absence_of` validation.
class PowerHungryCountry include ActiveModel::Model attr_accessor :nuclear_weapons validates_absence_of :nuclear_weapons end # RSpec RSpec.describe PowerHungryCountry, type: :model do it { should validate_absence_of(:nuclear_weapons) } end # Minitest (Shoulda) class PowerHungryCountryTest < ActiveSupport::TestCase should validate_absence_of(:nuclear_weapons) end
#### Qualifiers
##### on
Use ‘on` if your validation applies only under a certain context.
class PowerHungryCountry include ActiveModel::Model attr_accessor :nuclear_weapons validates_absence_of :nuclear_weapons, on: :create end # RSpec RSpec.describe PowerHungryCountry, type: :model do it { should validate_absence_of(:nuclear_weapons).on(:create) } end # Minitest (Shoulda) class PowerHungryCountryTest < ActiveSupport::TestCase should validate_absence_of(:nuclear_weapons).on(:create) end
##### with_message
Use ‘with_message` if you are using a custom validation message.
class PowerHungryCountry include ActiveModel::Model attr_accessor :nuclear_weapons validates_absence_of :nuclear_weapons, message: "there shall be peace on Earth" end # RSpec RSpec.describe PowerHungryCountry, type: :model do it do should validate_absence_of(:nuclear_weapons). with_message("there shall be peace on Earth") end end # Minitest (Shoulda) class PowerHungryCountryTest < ActiveSupport::TestCase should validate_absence_of(:nuclear_weapons). with_message("there shall be peace on Earth") end
@return [ValidateAbsenceOfMatcher}
Source
# File lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb, line 78 def validate_acceptance_of(attr) ValidateAcceptanceOfMatcher.new(attr) end
The ‘validate_acceptance_of` matcher tests usage of the `validates_acceptance_of` validation.
class Registration include ActiveModel::Model attr_accessor :eula validates_acceptance_of :eula end # RSpec RSpec.describe Registration, type: :model do it { should validate_acceptance_of(:eula) } end # Minitest (Shoulda) class RegistrationTest < ActiveSupport::TestCase should validate_acceptance_of(:eula) end
#### Qualifiers
##### on
Use ‘on` if your validation applies only under a certain context.
class Registration include ActiveModel::Model attr_accessor :terms_of_service validates_acceptance_of :terms_of_service, on: :create end # RSpec RSpec.describe Registration, type: :model do it do should validate_acceptance_of(:terms_of_service). on(:create) end end # Minitest (Shoulda) class RegistrationTest < ActiveSupport::TestCase should validate_acceptance_of(:terms_of_service).on(:create) end
##### with_message
Use ‘with_message` if you are using a custom validation message.
class Registration include ActiveModel::Model attr_accessor :terms_of_service validates_acceptance_of :terms_of_service, message: 'You must accept the terms of service' end # RSpec RSpec.describe Registration, type: :model do it do should validate_acceptance_of(:terms_of_service). with_message('You must accept the terms of service') end end # Minitest (Shoulda) class RegistrationTest < ActiveSupport::TestCase should validate_acceptance_of(:terms_of_service). with_message('You must accept the terms of service') end
@return [ValidateAcceptanceOfMatcher]
Source
# File lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb, line 75 def validate_confirmation_of(attr) ValidateConfirmationOfMatcher.new(attr) end
The ‘validate_confirmation_of` matcher tests usage of the `validates_confirmation_of` validation.
class User include ActiveModel::Model attr_accessor :email validates_confirmation_of :email end # RSpec RSpec.describe User, type: :model do it { should validate_confirmation_of(:email) } end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should validate_confirmation_of(:email) end
#### Qualifiers
##### on
Use ‘on` if your validation applies only under a certain context.
class User include ActiveModel::Model attr_accessor :password validates_confirmation_of :password, on: :create end # RSpec RSpec.describe User, type: :model do it { should validate_confirmation_of(:password).on(:create) } end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should validate_confirmation_of(:password).on(:create) end
##### with_message
Use ‘with_message` if you are using a custom validation message.
class User include ActiveModel::Model attr_accessor :password validates_confirmation_of :password, message: 'Please re-enter your password' end # RSpec RSpec.describe User, type: :model do it do should validate_confirmation_of(:password). with_message('Please re-enter your password') end end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should validate_confirmation_of(:password). with_message('Please re-enter your password') end
@return [ValidateConfirmationOfMatcher]
Source
# File lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb, line 117 def validate_exclusion_of(attr) ValidateExclusionOfMatcher.new(attr) end
The ‘validate_exclusion_of` matcher tests usage of the `validates_exclusion_of` validation, asserting that an attribute cannot take a blacklist of values, and inversely, can take values outside of this list.
If your blacklist is an array of values, use ‘in_array`:
class Game include ActiveModel::Model attr_accessor :supported_os validates_exclusion_of :supported_os, in: ['Mac', 'Linux'] end # RSpec RSpec.describe Game, type: :model do it do should validate_exclusion_of(:supported_os). in_array(['Mac', 'Linux']) end end # Minitest (Shoulda) class GameTest < ActiveSupport::TestCase should validate_exclusion_of(:supported_os). in_array(['Mac', 'Linux']) end
If your blacklist is a range of values, use ‘in_range`:
class Game include ActiveModel::Model attr_accessor :supported_os validates_exclusion_of :supported_os, in: 5..8 end # RSpec RSpec.describe Game, type: :model do it do should validate_exclusion_of(:floors_with_enemies). in_range(5..8) end end # Minitest (Shoulda) class GameTest < ActiveSupport::TestCase should validate_exclusion_of(:floors_with_enemies). in_range(5..8) end
#### Qualifiers
##### on
Use ‘on` if your validation applies only under a certain context.
class Game include ActiveModel::Model attr_accessor :weapon validates_exclusion_of :weapon, in: ['pistol', 'paintball gun', 'stick'], on: :create end # RSpec RSpec.describe Game, type: :model do it do should validate_exclusion_of(:weapon). in_array(['pistol', 'paintball gun', 'stick']). on(:create) end end # Minitest (Shoulda) class GameTest < ActiveSupport::TestCase should validate_exclusion_of(:weapon). in_array(['pistol', 'paintball gun', 'stick']). on(:create) end
##### with_message
Use ‘with_message` if you are using a custom validation message.
class Game include ActiveModel::Model attr_accessor :weapon validates_exclusion_of :weapon, in: ['pistol', 'paintball gun', 'stick'], message: 'You chose a puny weapon' end # RSpec RSpec.describe Game, type: :model do it do should validate_exclusion_of(:weapon). in_array(['pistol', 'paintball gun', 'stick']). with_message('You chose a puny weapon') end end # Minitest (Shoulda) class GameTest < ActiveSupport::TestCase should validate_exclusion_of(:weapon). in_array(['pistol', 'paintball gun', 'stick']). with_message('You chose a puny weapon') end
@return [ValidateExclusionOfMatcher]
Source
# File lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb, line 265 def validate_inclusion_of(attr) ValidateInclusionOfMatcher.new(attr) end
The ‘validate_inclusion_of` matcher tests usage of the `validates_inclusion_of` validation, asserting that an attribute can take a whitelist of values and cannot take values outside of this list.
If your whitelist is an array of values, use ‘in_array`:
class Issue include ActiveModel::Model attr_accessor :state validates_inclusion_of :state, in: ['open', 'resolved', 'unresolved'] end # RSpec RSpec.describe Issue, type: :model do it do should validate_inclusion_of(:state). in_array(['open', 'resolved', 'unresolved']) end end # Minitest (Shoulda) class IssueTest < ActiveSupport::TestCase should validate_inclusion_of(:state). in_array(['open', 'resolved', 'unresolved']) end
If your whitelist is a range of values, use ‘in_range`:
class Issue include ActiveModel::Model attr_accessor :priority validates_inclusion_of :priority, in: 1..5 end # RSpec RSpec.describe Issue, type: :model do it { should validate_inclusion_of(:state).in_range(1..5) } end # Minitest (Shoulda) class IssueTest < ActiveSupport::TestCase should validate_inclusion_of(:state).in_range(1..5) end
#### Caveats
We discourage using ‘validate_inclusion_of` with boolean columns. In fact, there is never a case where a boolean column will be anything but true, false, or nil, as ActiveRecord
will type-cast an incoming value to one of these three values. That means there isn’t any way we can refute this logic in a test. Hence, this will produce a warning:
it do should validate_inclusion_of(:imported). in_array([true, false]) end
The only case where ‘validate_inclusion_of` could be appropriate is for ensuring that a boolean column accepts nil, but we recommend using `allow_value` instead, like this:
it { should allow_value(nil).for(:imported) }
#### Qualifiers
Use ‘on` if your validation applies only under a certain context.
class Issue include ActiveModel::Model attr_accessor :severity validates_inclusion_of :severity, in: %w(low medium high), on: :create end # RSpec RSpec.describe Issue, type: :model do it do should validate_inclusion_of(:severity). in_array(%w(low medium high)). on(:create) end end # Minitest (Shoulda) class IssueTest < ActiveSupport::TestCase should validate_inclusion_of(:severity). in_array(%w(low medium high)). on(:create) end
##### with_message
Use ‘with_message` if you are using a custom validation message.
class Issue include ActiveModel::Model attr_accessor :severity validates_inclusion_of :severity, in: %w(low medium high), message: 'Severity must be low, medium, or high' end # RSpec RSpec.describe Issue, type: :model do it do should validate_inclusion_of(:severity). in_array(%w(low medium high)). with_message('Severity must be low, medium, or high') end end # Minitest (Shoulda) class IssueTest < ActiveSupport::TestCase should validate_inclusion_of(:severity). in_array(%w(low medium high)). with_message('Severity must be low, medium, or high') end
##### with_low_message
Use ‘with_low_message` if you have a custom validation message for when a given value is too low.
class Person include ActiveModel::Model attr_accessor :age validate :age_must_be_valid private def age_must_be_valid if age < 65 self.errors.add :age, 'You do not receive any benefits' end end end # RSpec RSpec.describe Person, type: :model do it do should validate_inclusion_of(:age). in_range(0..65). with_low_message('You do not receive any benefits') end end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_inclusion_of(:age). in_range(0..65). with_low_message('You do not receive any benefits') end
##### with_high_message
Use ‘with_high_message` if you have a custom validation message for when a given value is too high.
class Person include ActiveModel::Model attr_accessor :age validate :age_must_be_valid private def age_must_be_valid if age > 21 self.errors.add :age, "You're too old for this stuff" end end end # RSpec RSpec.describe Person, type: :model do it do should validate_inclusion_of(:age). in_range(0..21). with_high_message("You're too old for this stuff") end end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_inclusion_of(:age). in_range(0..21). with_high_message("You're too old for this stuff") end
##### allow_nil
Use ‘allow_nil` to assert that the attribute allows nil.
class Issue include ActiveModel::Model attr_accessor :state validates_presence_of :state validates_inclusion_of :state, in: ['open', 'resolved', 'unresolved'], allow_nil: true end # RSpec RSpec.describe Issue, type: :model do it do should validate_inclusion_of(:state). in_array(['open', 'resolved', 'unresolved']). allow_nil end end # Minitest (Shoulda) class IssueTest < ActiveSupport::TestCase should validate_inclusion_of(:state). in_array(['open', 'resolved', 'unresolved']). allow_nil end
##### allow_blank
Use ‘allow_blank` to assert that the attribute allows blank.
class Issue include ActiveModel::Model attr_accessor :state validates_presence_of :state validates_inclusion_of :state, in: ['open', 'resolved', 'unresolved'], allow_blank: true end # RSpec RSpec.describe Issue, type: :model do it do should validate_inclusion_of(:state). in_array(['open', 'resolved', 'unresolved']). allow_blank end end # Minitest (Shoulda) class IssueTest < ActiveSupport::TestCase should validate_inclusion_of(:state). in_array(['open', 'resolved', 'unresolved']). allow_blank end
@return [ValidateInclusionOfMatcher]
Source
# File lib/shoulda/matchers/active_model/validate_length_of_matcher.rb, line 263 def validate_length_of(attr) ValidateLengthOfMatcher.new(attr) end
The ‘validate_length_of` matcher tests usage of the `validates_length_of` matcher. Note that this matcher is intended to be used against string columns and not integer columns.
#### Qualifiers
Use ‘on` if your validation applies only under a certain context.
class User include ActiveModel::Model attr_accessor :password validates_length_of :password, minimum: 10, on: :create end # RSpec RSpec.describe User, type: :model do it do should validate_length_of(:password). is_at_least(10). on(:create) end end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should validate_length_of(:password). is_at_least(10). on(:create) end
##### is_at_least
Use ‘is_at_least` to test usage of the `:minimum` option. This asserts that the attribute can take a string which is equal to or longer than the given length and cannot take a string which is shorter.
class User include ActiveModel::Model attr_accessor :bio validates_length_of :bio, minimum: 15 end # RSpec RSpec.describe User, type: :model do it { should validate_length_of(:bio).is_at_least(15) } end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should validate_length_of(:bio).is_at_least(15) end
##### is_at_most
Use ‘is_at_most` to test usage of the `:maximum` option. This asserts that the attribute can take a string which is equal to or shorter than the given length and cannot take a string which is longer.
class User include ActiveModel::Model attr_accessor :status_update validates_length_of :status_update, maximum: 140 end # RSpec RSpec.describe User, type: :model do it { should validate_length_of(:status_update).is_at_most(140) } end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should validate_length_of(:status_update).is_at_most(140) end
##### is_equal_to
Use ‘is_equal_to` to test usage of the `:is` option. This asserts that the attribute can take a string which is exactly equal to the given length and cannot take a string which is shorter or longer.
class User include ActiveModel::Model attr_accessor :favorite_superhero validates_length_of :favorite_superhero, is: 6 end # RSpec RSpec.describe User, type: :model do it { should validate_length_of(:favorite_superhero).is_equal_to(6) } end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should validate_length_of(:favorite_superhero).is_equal_to(6) end
##### is_at_least + is_at_most
Use ‘is_at_least` and `is_at_most` together to test usage of the `:in` option.
class User include ActiveModel::Model attr_accessor :password validates_length_of :password, in: 5..30 end # RSpec RSpec.describe User, type: :model do it do should validate_length_of(:password). is_at_least(5).is_at_most(30) end end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should validate_length_of(:password). is_at_least(5).is_at_most(30) end
##### with_message
Use ‘with_message` if you are using a custom validation message.
class User include ActiveModel::Model attr_accessor :password validates_length_of :password, minimum: 10, message: "Password isn't long enough" end # RSpec RSpec.describe User, type: :model do it do should validate_length_of(:password). is_at_least(10). with_message("Password isn't long enough") end end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should validate_length_of(:password). is_at_least(10). with_message("Password isn't long enough") end
##### with_short_message
Use ‘with_short_message` if you are using a custom “too short” message.
class User include ActiveModel::Model attr_accessor :secret_key validates_length_of :secret_key, in: 15..100, too_short: 'Secret key must be more than 15 characters' end # RSpec RSpec.describe User, type: :model do it do should validate_length_of(:secret_key). is_at_least(15). with_short_message('Secret key must be more than 15 characters') end end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should validate_length_of(:secret_key). is_at_least(15). with_short_message('Secret key must be more than 15 characters') end
##### with_long_message
Use ‘with_long_message` if you are using a custom “too long” message.
class User include ActiveModel::Model attr_accessor :secret_key validates_length_of :secret_key, in: 15..100, too_long: 'Secret key must be less than 100 characters' end # RSpec RSpec.describe User, type: :model do it do should validate_length_of(:secret_key). is_at_most(100). with_long_message('Secret key must be less than 100 characters') end end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should validate_length_of(:secret_key). is_at_most(100). with_long_message('Secret key must be less than 100 characters') end
##### allow_nil
Use ‘allow_nil` to assert that the attribute allows nil.
class User include ActiveModel::Model attr_accessor :bio validates_length_of :bio, minimum: 15, allow_nil: true end # RSpec describe User do it { should validate_length_of(:bio).is_at_least(15).allow_nil } end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should validate_length_of(:bio).is_at_least(15).allow_nil end
@return [ValidateLengthOfMatcher]
# ##### allow_blank
Use ‘allow_blank` to assert that the attribute allows blank.
class User include ActiveModel::Model attr_accessor :bio validates_length_of :bio, minimum: 15, allow_blank: true end # RSpec describe User do it { should validate_length_of(:bio).is_at_least(15).allow_blank } end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should validate_length_of(:bio).is_at_least(15).allow_blank end
Source
# File lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb, line 328 def validate_numericality_of(attr) ValidateNumericalityOfMatcher.new(attr) end
The ‘validate_numericality_of` matcher tests usage of the `validates_numericality_of` validation.
class Person include ActiveModel::Model attr_accessor :gpa validates_numericality_of :gpa end # RSpec RSpec.describe Person, type: :model do it { should validate_numericality_of(:gpa) } end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_numericality_of(:gpa) end
#### Qualifiers
##### on
Use ‘on` if your validation applies only under a certain context.
class Person include ActiveModel::Model attr_accessor :number_of_dependents validates_numericality_of :number_of_dependents, on: :create end # RSpec RSpec.describe Person, type: :model do it do should validate_numericality_of(:number_of_dependents). on(:create) end end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_numericality_of(:number_of_dependents).on(:create) end
##### only_integer
Use ‘only_integer` to test usage of the `:only_integer` option. This asserts that your attribute only allows integer numbers and disallows non-integer ones.
class Person include ActiveModel::Model attr_accessor :age validates_numericality_of :age, only_integer: true end # RSpec RSpec.describe Person, type: :model do it { should validate_numericality_of(:age).only_integer } end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_numericality_of(:age).only_integer end
##### is_less_than
Use ‘is_less_than` to test usage of the the `:less_than` option. This asserts that the attribute can take a number which is less than the given value and cannot take a number which is greater than or equal to it.
class Person include ActiveModel::Model attr_accessor :number_of_cars validates_numericality_of :number_of_cars, less_than: 2 end # RSpec RSpec.describe Person, type: :model do it do should validate_numericality_of(:number_of_cars). is_less_than(2) end end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_numericality_of(:number_of_cars). is_less_than(2) end
##### is_less_than_or_equal_to
Use ‘is_less_than_or_equal_to` to test usage of the `:less_than_or_equal_to` option. This asserts that the attribute can take a number which is less than or equal to the given value and cannot take a number which is greater than it.
class Person include ActiveModel::Model attr_accessor :birth_year validates_numericality_of :birth_year, less_than_or_equal_to: 1987 end # RSpec RSpec.describe Person, type: :model do it do should validate_numericality_of(:birth_year). is_less_than_or_equal_to(1987) end end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_numericality_of(:birth_year). is_less_than_or_equal_to(1987) end
##### is_equal_to
Use ‘is_equal_to` to test usage of the `:equal_to` option. This asserts that the attribute can take a number which is equal to the given value and cannot take a number which is not equal.
class Person include ActiveModel::Model attr_accessor :weight validates_numericality_of :weight, equal_to: 150 end # RSpec RSpec.describe Person, type: :model do it { should validate_numericality_of(:weight).is_equal_to(150) } end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_numericality_of(:weight).is_equal_to(150) end
##### is_greater_than_or_equal_to
Use ‘is_greater_than_or_equal_to` to test usage of the `:greater_than_or_equal_to` option. This asserts that the attribute can take a number which is greater than or equal to the given value and cannot take a number which is less than it.
class Person include ActiveModel::Model attr_accessor :height validates_numericality_of :height, greater_than_or_equal_to: 55 end # RSpec RSpec.describe Person, type: :model do it do should validate_numericality_of(:height). is_greater_than_or_equal_to(55) end end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_numericality_of(:height). is_greater_than_or_equal_to(55) end
##### is_greater_than
Use ‘is_greater_than` to test usage of the `:greater_than` option. This asserts that the attribute can take a number which is greater than the given value and cannot take a number less than or equal to it.
class Person include ActiveModel::Model attr_accessor :legal_age validates_numericality_of :legal_age, greater_than: 21 end # RSpec RSpec.describe Person, type: :model do it do should validate_numericality_of(:legal_age). is_greater_than(21) end end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_numericality_of(:legal_age). is_greater_than(21) end
##### is_other_than
Use ‘is_other_than` to test usage of the `:other_than` option. This asserts that the attribute can take a number which is not equal to the given value.
class Person include ActiveModel::Model attr_accessor :legal_age validates_numericality_of :legal_age, other_than: 21 end # RSpec RSpec.describe Person, type: :model do it do should validate_numericality_of(:legal_age). is_other_than(21) end end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_numericality_of(:legal_age). is_other_than(21) end
##### even
Use ‘even` to test usage of the `:even` option. This asserts that the attribute can take odd numbers and cannot take even ones.
class Person include ActiveModel::Model attr_accessor :birth_month validates_numericality_of :birth_month, even: true end # RSpec RSpec.describe Person, type: :model do it { should validate_numericality_of(:birth_month).even } end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_numericality_of(:birth_month).even end
##### odd
Use ‘odd` to test usage of the `:odd` option. This asserts that the attribute can take a number which is odd and cannot take a number which is even.
class Person include ActiveModel::Model attr_accessor :birth_day validates_numericality_of :birth_day, odd: true end # RSpec RSpec.describe Person, type: :model do it { should validate_numericality_of(:birth_day).odd } end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_numericality_of(:birth_day).odd end
##### with_message
Use ‘with_message` if you are using a custom validation message.
class Person include ActiveModel::Model attr_accessor :number_of_dependents validates_numericality_of :number_of_dependents, message: 'Number of dependents must be a number' end # RSpec RSpec.describe Person, type: :model do it do should validate_numericality_of(:number_of_dependents). with_message('Number of dependents must be a number') end end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should validate_numericality_of(:number_of_dependents). with_message('Number of dependents must be a number') end
##### allow_nil
Use ‘allow_nil` to assert that the attribute allows nil.
class Post include ActiveModel::Model attr_accessor :age validates_numericality_of :age, allow_nil: true end # RSpec RSpec.describe Post, type: :model do it { should validate_numericality_of(:age).allow_nil } end # Minitest (Shoulda) class PostTest < ActiveSupport::TestCase should validate_numericality_of(:age).allow_nil end
@return [ValidateNumericalityOfMatcher]
Source
# File lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb, line 129 def validate_presence_of(attr) ValidatePresenceOfMatcher.new(attr) end
The ‘validate_presence_of` matcher tests usage of the `validates_presence_of` validation.
class Robot include ActiveModel::Model attr_accessor :arms validates_presence_of :arms end # RSpec RSpec.describe Robot, type: :model do it { should validate_presence_of(:arms) } end # Minitest (Shoulda) class RobotTest < ActiveSupport::TestCase should validate_presence_of(:arms) end
#### Caveats
Under Rails 4 and greater, if your model ‘has_secure_password` and you are validating presence of the password using a record whose password has already been set prior to calling the matcher, you will be instructed to use a record whose password is empty instead.
For example, given this scenario:
class User < ActiveRecord::Base has_secure_password validations: false validates_presence_of :password end RSpec.describe User, type: :model do subject { User.new(password: '123456') } it { should validate_presence_of(:password) } end
the above test will raise an error like this:
The validation failed because your User model declares `has_secure_password`, and `validate_presence_of` was called on a user which has `password` already set to a value. Please use a user with an empty `password` instead.
This happens because ‘has_secure_password` itself overrides your model so that it is impossible to set `password` to nil. This means that it is impossible to test that setting `password` to nil places your model in an invalid state (which in turn means that the validation itself is unnecessary).
#### Qualifiers
##### allow_nil
Use ‘allow_nil` if your model has an optional attribute.
class Robot include ActiveModel::Model attr_accessor :nickname validates_presence_of :nickname, allow_nil: true end # RSpec RSpec.describe Robot, type: :model do it { should validate_presence_of(:nickname).allow_nil } end # Minitest (Shoulda) class RobotTest < ActiveSupport::TestCase should validate_presence_of(:nickname).allow_nil end
##### on
Use ‘on` if your validation applies only under a certain context.
class Robot include ActiveModel::Model attr_accessor :arms validates_presence_of :arms, on: :create end # RSpec RSpec.describe Robot, type: :model do it { should validate_presence_of(:arms).on(:create) } end # Minitest (Shoulda) class RobotTest < ActiveSupport::TestCase should validate_presence_of(:arms).on(:create) end
##### with_message
Use ‘with_message` if you are using a custom validation message.
class Robot include ActiveModel::Model attr_accessor :legs validates_presence_of :legs, message: 'Robot has no legs' end # RSpec RSpec.describe Robot, type: :model do it do should validate_presence_of(:legs). with_message('Robot has no legs') end end # Minitest (Shoulda) class RobotTest < ActiveSupport::TestCase should validate_presence_of(:legs). with_message('Robot has no legs') end
@return [ValidatePresenceOfMatcher]