Module: Shoulda::Matchers::ActiveModel

Defined in:
lib/shoulda/matchers/active_model.rb,
lib/shoulda/matchers/active_model/errors.rb,
lib/shoulda/matchers/active_model/helpers.rb,
lib/shoulda/matchers/active_model/validation_matcher.rb,
lib/shoulda/matchers/active_model/allow_value_matcher.rb,
lib/shoulda/matchers/active_model/numericality_matchers.rb,
lib/shoulda/matchers/active_model/disallow_value_matcher.rb,
lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb,
lib/shoulda/matchers/active_model/exception_message_finder.rb,
lib/shoulda/matchers/active_model/validation_message_finder.rb,
lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb,
lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb,
lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb,
lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb,
lib/shoulda/matchers/active_model/have_secure_password_matcher.rb,
lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb,
lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb,
lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb,
lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb,
lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb,
lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb,
lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb,
lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb,
lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb,
lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb

Instance Method Summary (collapse)

Instance Method Details

- (AllowMassAssignmentOfMatcher) allow_mass_assignment_of(value)

The allow_mass_assignment_of matcher tests usage of Rails 3's attr_accessible and attr_protected macros, asserting that an attribute in your model is contained in either the whitelist or blacklist and thus can or cannot be set via mass assignment.

class Post
  include ActiveModel::Model
  include ActiveModel::MassAssignmentSecurity
  attr_accessor :title

  attr_accessible :title
end

class User
  include ActiveModel::Model
  include ActiveModel::MassAssignmentSecurity
  attr_accessor :encrypted_password

  attr_protected :encrypted_password
end

# RSpec
describe Post do
  it { should allow_mass_assignment_of(:title) }
end

describe User do
  it { should_not allow_mass_assignment_of(:encrypted_password) }
end

# Test::Unit
class PostTest < ActiveSupport::TestCase
  should allow_mass_assignment_of(:title)
end

class UserTest < ActiveSupport::TestCase
  should_not allow_mass_assignment_of(:encrypted_password)
end

Optional qualifiers

as

Use as if your mass-assignment rules apply only under a certain role (Rails >= 3.1 only).

class Post
  include ActiveModel::Model
  include ActiveModel::MassAssignmentSecurity
  attr_accessor :title

  attr_accessible :title, as: :admin
end

# RSpec
describe Post do
  it { should allow_mass_assignment_of(:title).as(:admin) }
end

# Test::Unit
class PostTest < ActiveSupport::TestCase
  should allow_mass_assignment_of(:title).as(:admin)
end


70
71
72
# File 'lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb', line 70

def allow_mass_assignment_of(value)
  AllowMassAssignmentOfMatcher.new(value)
end

- (AllowValueMatcher) allow_value(*values)

The allow_value matcher is used to test that an attribute of a model can or cannot be set to a particular value or values. It is most commonly used in conjunction with the validates_format_of validation.

should

In the positive form, allow_value asserts that an attribute can be set to one or more values, succeeding if none of the values cause the record to be invalid:

class UserProfile
  include ActiveModel::Model
  attr_accessor :website_url

  validates_format_of :website_url, with: URI.regexp
end

# RSpec
describe UserProfile do
  it do
    should allow_value('http://foo.com', 'http://bar.com/baz').
      for(:website_url)
  end
end

# Test::Unit
class UserProfileTest < ActiveSupport::TestCase
  should allow_value('http://foo.com', 'http://bar.com/baz').
    for(:website_url)
end

should_not

In the negative form, allow_value asserts that an attribute cannot be set to one or more values, succeeding if the first value causes the record to be invalid.

This can be surprising so in this case if you need to check that all of the values are invalid, use separate assertions:

class UserProfile
  include ActiveModel::Model
  attr_accessor :website_url

  validates_format_of :website_url, with: URI.regexp
end

describe UserProfile do
  # One assertion: 'buz' and 'bar' will not be tested
  it { should_not allow_value('fiz', 'buz', 'bar').for(:website_url) }

  # Three assertions, all tested separately
  it { should_not allow_value('fiz').for(:website_url) }
  it { should_not allow_value('buz').for(:website_url) }
  it { should_not allow_value('bar').for(:website_url) }
end

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
describe UserProfile do
  it do
    should allow_value('2013-01-01').
      for(:birthday_as_string).
      on(:create)
  end
end

# Test::Unit
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
describe UserProfile do
  it do
    should allow_value('open', 'closed').
      for(:state).
      with_message('State must be open or closed')
  end
end

# Test::Unit
class UserProfileTest < ActiveSupport::TestCase
  should allow_value('open', 'closed').
    for(:state).
    with_message('State must be 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
describe UserProfile 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

# Test::Unit
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


163
164
165
166
167
168
169
# File 'lib/shoulda/matchers/active_model/allow_value_matcher.rb', line 163

def allow_value(*values)
  if values.empty?
    raise ArgumentError, 'need at least one argument'
  else
    AllowValueMatcher.new(*values)
  end
end

- (EnsureExclusionOfMatcher) ensure_exclusion_of(attr)

The ensure_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
describe Game do
  it do
    should ensure_exclusion_of(:supported_os).
      in_array(['Mac', 'Linux'])
  end
end

# Test::Unit
class GameTest < ActiveSupport::TestCase
  should ensure_exclusion_of(:supported_os).
    in_array(['Mac', 'Linux'])
end

If your blacklist is a range of values, use in_rnage:

class Game
  include ActiveModel::Model
  attr_accessor :supported_os

  validates_exclusion_of :supported_os, in: ['Mac', 'Linux']
end

# RSpec
describe Game do
  it do
    should ensure_exclusion_of(:floors_with_enemies).
      in_range(5..8)
  end
end

# Test::Unit
class GameTest < ActiveSupport::TestCase
  should ensure_exclusion_of(:floors_with_enemies).
    in_range(5..8)
end

Qualifiers

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
describe Game do
  it do
    should ensure_exclusion_of(:weapon).
      in_array(['pistol', 'paintball gun', 'stick']).
      with_message('You chose a puny weapon')
  end
end

# Test::Unit
class GameTest < ActiveSupport::TestCase
  should ensure_exclusion_of(:weapon).
    in_array(['pistol', 'paintball gun', 'stick']).
    with_message('You chose a puny weapon')
end


88
89
90
# File 'lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb', line 88

def ensure_exclusion_of(attr)
  EnsureExclusionOfMatcher.new(attr)
end

- (EnsureInclusionOfMatcher) ensure_inclusion_of(attr)

The ensure_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: %w(open resolved unresolved)
end

# RSpec
describe Issue do
  it do
    should ensure_inclusion_of(:state).
      in_array(%w(open resolved unresolved))
  end
end

# Test::Unit
class IssueTest < ActiveSupport::TestCase
  should ensure_inclusion_of(:state).
    in_array(%w(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
describe Issue do
  it { should ensure_inclusion_of(:state).in_range(1..5) }
end

# Test::Unit
class IssueTest < ActiveSupport::TestCase
  should ensure_inclusion_of(:state).in_range(1..5)
end

Optional qualifiers

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
describe Issue do
  it do
    should ensure_inclusion_of(:severity).
      in_array(%w(low medium high)).
      with_message('Severity must be low, medium, or high')
  end
end

# Test::Unit
class IssueTest < ActiveSupport::TestCase
  should ensure_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
describe Person do
  it do
    should ensure_inclusion_of(:age).
      in_range(0..65).
      with_low_message('You do not receive any benefits')
  end
end

# Test::Unit
class PersonTest < ActiveSupport::TestCase
  should ensure_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
describe Person do
  it do
    should ensure_inclusion_of(:age).
      in_range(0..21).
      with_high_message("You're too old for this stuff")
  end
end

# Test::Unit
class PersonTest < ActiveSupport::TestCase
  should ensure_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: %w(open resolved unresolved),
    allow_nil: true
end

# RSpec
describe Issue do
  it do
    should ensure_inclusion_of(:state).
      in_array(%w(open resolved unresolved)).
      allow_nil
  end
end

# Test::Unit
class IssueTest < ActiveSupport::TestCase
  should ensure_inclusion_of(:state).
    in_array(%w(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: %w(open resolved unresolved),
    allow_blank: true
end

# RSpec
describe Issue do
  it do
    should ensure_inclusion_of(:state).
      in_array(%w(open resolved unresolved)).
      allow_blank
  end
end

# Test::Unit
class IssueTest < ActiveSupport::TestCase
  should ensure_inclusion_of(:state).
    in_array(%w(open resolved unresolved)).
    allow_blank
end


217
218
219
# File 'lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb', line 217

def ensure_inclusion_of(attr)
  EnsureInclusionOfMatcher.new(attr)
end

- (EnsureLengthOfMatcher) ensure_length_of(attr)

The ensure_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

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

describe User do
  it { should ensure_length_of(:bio).is_at_least(15) }
end

# Test::Unit

class UserTest < ActiveSupport::TestCase
  should ensure_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
describe User do
  it { should ensure_length_of(:status_update).is_at_most(140) }
end

# Test::Unit
class UserTest < ActiveSupport::TestCase
  should ensure_length_of(:status_update).is_at_most(140)
end
is_equal_to

Use is_at_equal 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
describe User do
  it { should ensure_length_of(:favorite_superhero).is_equal_to(6) }
end

# Test::Unit
class UserTest < ActiveSupport::TestCase
  should ensure_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
describe User do
  it do
    should ensure_length_of(:password).
      is_at_least(5).is_at_most(30)
  end
end

# Test::Unit
class UserTest < ActiveSupport::TestCase
  should ensure_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 :api_token

  validates_length_of :api_token,
    minimum: 10,
    message: "Password isn't long enough"
end

# RSpec
describe User do
  it do
    should ensure_length_of(:password).
      is_at_least(10).
      with_message("Password isn't long enough")
  end
end

# Test::Unit
class UserTest < ActiveSupport::TestCase
  should ensure_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
describe User do
  it do
    should ensure_length_of(:secret_key).
      is_at_least(15).
      with_short_message('Secret key must be more than 15 characters')
  end
end

# Test::Unit
class UserTest < ActiveSupport::TestCase
  should ensure_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
describe User do
  it do
    should ensure_length_of(:secret_key).
      is_at_most(100).
      with_long_message('Secret key must be less than 100 characters')
  end
end

# Test::Unit
class UserTest < ActiveSupport::TestCase
  should ensure_length_of(:secret_key).
    is_at_most(100).
    with_long_message('Secret key must be less than 100 characters')
end


196
197
198
# File 'lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb', line 196

def ensure_length_of(attr)
  EnsureLengthOfMatcher.new(attr)
end

- (HaveSecurePasswordMatcher) have_secure_password

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

  has_secure_password
end

# RSpec
describe User do
  it { should have_secure_password }
end

# Test::Unit
class UserTest < ActiveSupport::TestCase
  should have_secure_password
end


29
30
31
# File 'lib/shoulda/matchers/active_model/have_secure_password_matcher.rb', line 29

def have_secure_password
  HaveSecurePasswordMatcher.new
end

- (ValidateAbsenceOfMatcher) validate_absence_of(attr)

The validate_absence_of matcher tests the usage of the validates_absence_of validation.

class Artillery
  include ActiveModel::Model
  attr_accessor :arms

  validates_absence_of :arms
end

# RSpec
describe Artillery do
  it { should validate_absence_of(:arms) }
end

# Test::Unit
class ArtilleryTest < ActiveSupport::TestCase
  should validate_absence_of(:arms)
end

Qualifiers

with_message

Use with_message if you are using a custom validation message.

class Artillery
  include ActiveModel::Model
  attr_accessor :arms

  validates_absence_of :arms,
    message: "We're fresh outta arms here, soldier!"
end

# RSpec
describe Artillery do
  it do
    should validate_absence_of(:arms).
      with_message("We're fresh outta arms here, soldier!")
  end
end

# Test::Unit
class ArtilleryTest < ActiveSupport::TestCase
  should validate_absence_of(:arms).
    with_message("We're fresh outta arms here, soldier!")
end


54
55
56
# File 'lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb', line 54

def validate_absence_of(attr)
  ValidateAbsenceOfMatcher.new(attr)
end

- (ValidateAcceptanceOfMatcher) validate_acceptance_of(attr)

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
describe Registration do
  it { should validate_acceptance_of(:eula) }
end

# Test::Unit
class RegistrationTest < ActiveSupport::TestCase
  should validate_acceptance_of(:eula)
end

Qualifiers

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
describe Registration do
  it do
    should validate_acceptance_of(:terms_of_service).
      with_message('You must accept the terms of service')
  end
end

# Test::Unit
class RegistrationTest < ActiveSupport::TestCase
  should validate_acceptance_of(:terms_of_service).
    with_message('You must accept the terms of service')
end


54
55
56
# File 'lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb', line 54

def validate_acceptance_of(attr)
  ValidateAcceptanceOfMatcher.new(attr)
end

- (ValidateConfirmationOfMatcher) validate_confirmation_of(attr)

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
describe User do
  it { should validate_confirmation_of(:email) }
end

# Test::Unit
class UserTest < ActiveSupport::TestCase
  should validate_confirmation_of(:email)
end

Qualifiers

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
describe User do
  it do
    should validate_confirmation_of(:password).
      with_message('Please re-enter your password')
  end
end

# Test::Unit
class UserTest < ActiveSupport::TestCase
  should validate_confirmation_of(:password).
    with_message('Please re-enter your password')
end


54
55
56
# File 'lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb', line 54

def validate_confirmation_of(attr)
  ValidateConfirmationOfMatcher.new(attr)
end

- (ValidateNumericalityOfMatcher) validate_numericality_of(attr)

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
describe Person do
  it { should validate_numericality_of(:gpa) }
end

# Test::Unit
class PersonTest < ActiveSupport::TestCase
  should validate_numericality_of(:gpa)
end

Qualifiers

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
describe Person do
  it { should validate_numericality_of(:age).only_integer }
end

# Test::Unit
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
describe Person do
  it do
    should validate_numericality_of(:number_of_cars).
      is_less_than(2)
  end
end

# Test::Unit
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
describe Person do
  it do
    should validate_numericality_of(:birth_year).
      is_less_than_or_equal_to(1987)
  end
end

# Test::Unit
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
describe Person do
  it { should validate_numericality_of(:weight).is_equal_to(150) }
end

# Test::Unit
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
describe Person do
  it do
    should validate_numericality_of(:height).
      is_greater_than_or_equal_to(55)
  end
end

# Test::Unit
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 tthe :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
describe Person do
  it do
    should validate_numericality_of(:legal_age).
      is_greater_than(21)
  end
end

# Test::Unit
class PersonTest < ActiveSupport::TestCase
  should validate_numericality_of(:legal_age).
    is_greater_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
describe Person do
  it { should validate_numericality_of(:birth_month).even }
end

# Test::Unit
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
describe Person do
  it { should validate_numericality_of(:birth_day).odd }
end

# Test::Unit
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
describe Person do
  it do
    should validate_numericality_of(:number_of_dependents).
      with_message('Number of dependents must be a number')
  end
end

# Test::Unit
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 Age
  include ActiveModel::Model
  attr_accessor :age

  validates_numericality_of :age, allow_nil: true
end

# RSpec
describe Post do
  it { should validate_numericality_of(:age).allow_nil }
end

# Test::Unit
class PostTest < ActiveSupport::TestCase
  should validate_numericality_of(:age).allow_nil
end


277
278
279
# File 'lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb', line 277

def validate_numericality_of(attr)
  ValidateNumericalityOfMatcher.new(attr)
end

- (ValidatePresenceOfMatcher) validate_presence_of(attr)

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
describe Robot do
  it { should validate_presence_of(:arms) }
end

# Test::Unit
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

describe User 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

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
describe Robot do
  it do
    should validate_presence_of(:legs).
      with_message('Robot has no legs')
  end
end

# Test::Unit
class RobotTest < ActiveSupport::TestCase
  should validate_presence_of(:legs).
    with_message('Robot has no legs')
end


87
88
89
# File 'lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb', line 87

def validate_presence_of(attr)
  ValidatePresenceOfMatcher.new(attr)
end

- (ValidateUniquenessOfMatcher) validate_uniqueness_of(attr)

The validate_uniqueness_of matcher tests usage of the validates_uniqueness_of validation. It first checks for an existing instance of your model in the database, creating one if necessary. It then takes a new record and asserts that it fails validation if the attribute or attributes you've specified in the validation are set to values which are the same as those of the pre-existing record (thereby failing the uniqueness check).

class Post < ActiveRecord::Base
  validates_uniqueness_of :permalink
end

# RSpec
describe Post do
  it { should validate_uniqueness_of(:permalink) }
end

# Test::Unit
class PostTest < ActiveSupport::TestCase
  should validate_uniqueness_of(:permalink)
end

Caveat

This matcher works a bit differently than other matchers. As noted before, it will create an instance of your model if one doesn't already exist. Sometimes this step fails, especially if you have database-level restrictions on any attributes other than the one which is unique. In this case, the solution is to create a record manually before you call validate_uniqueness_of.

For example, say you have the following migration and model:

class CreatePosts < ActiveRecord::Migration
  def change
    create_table :posts do |t|
      t.string :title
      t.text :content, null: false
    end
  end
end

class Post < ActiveRecord::Base
  validates :title, uniqueness: true
end

You may be tempted to test the model like this:

describe Post do
  it { should validate_uniqueness_of(:title) }
end

However, running this test will fail with something like:

Failures:

  1) Post should require case sensitive unique value for title
     Failure/Error: it { should validate_uniqueness_of(:title) }
     ActiveRecord::StatementInvalid:
       SQLite3::ConstraintException: posts.content may not be NULL: INSERT INTO "posts" ("title") VALUES (?)

To fix this, you'll need to write this instead:

describe Post do
  it do
    Post.create!(content: 'Here is the content')
    should validate_uniqueness_of(:title)
  end
end

Or, if you're using FactoryGirl and you have a post factory defined which automatically sets content, you can say:

describe Post do
  it do
    FactoryGirl.create(:post)
    should validate_uniqueness_of(:title)
  end
end

Qualifiers

with_message

Use with_message if you are using a custom validation message.

class Post < ActiveRecord::Base
  validates_uniqueness_of :title, message: 'Please choose another title'
end

# RSpec
describe Post do
  it do
    should validate_uniqueness_of(:title).
      with_message('Please choose another title')
  end
end

# Test::Unit
class PostTest < ActiveSupport::TestCase
  should validate_uniqueness_of(:title).
    with_message('Please choose another title')
end
scoped_to

Use scoped_to to test usage of the :scope option. This asserts that a new record fails validation if not only the primary attribute is not unique, but the scoped attributes are not unique either.

class Post < ActiveRecord::Base
  validates_uniqueness_of :slug, scope: :user_id
end

# RSpec
describe Post do
  it { should validate_uniqueness_of(:slug).scoped_to(:journal_id) }
end

# Test::Unit
class PostTest < ActiveSupport::TestCase
  should validate_uniqueness_of(:slug).scoped_to(:journal_id)
end
case_insensitive

Use case_insensitive to test usage of the :case_sensitive option with a false value. This asserts that the uniquable attributes fail validation even if their values are a different case than corresponding attributes in the pre-existing record.

class Post < ActiveRecord::Base
  validates_uniqueness_of :key, case_sensitive: false
end

# RSpec
describe Post do
  it { should validate_uniqueness_of(:key).case_insensitive }
end

# Test::Unit
class PostTest < ActiveSupport::TestCase
  should validate_uniqueness_of(:key).case_insensitive
end
allow_nil

Use allow_nil to assert that the attribute allows nil.

class Post < ActiveRecord::Base
  validates_uniqueness_of :author_id, allow_nil: true
end

# RSpec
describe Post do
  it { should validate_uniqueness_of(:author_id).allow_nil }
end

# Test::Unit
class PostTest < ActiveSupport::TestCase
  should validate_uniqueness_of(:author_id).allow_nil
end


170
171
172
# File 'lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb', line 170

def validate_uniqueness_of(attr)
  ValidateUniquenessOfMatcher.new(attr)
end