module Shoulda::Matchers::ActiveRecord

Public Instance Methods

accept_nested_attributes_for(name) click to toggle source

The `accept_nested_attributes_for` matcher tests usage of the `accepts_nested_attributes_for` macro.

class Car < ActiveRecord::Base
  accepts_nested_attributes_for :doors
end

# RSpec
RSpec.describe Car, type: :model do
  it { should accept_nested_attributes_for(:doors) }
end

# Minitest (Shoulda) (using Shoulda)
class CarTest < ActiveSupport::TestCase
  should accept_nested_attributes_for(:doors)
end

#### Qualifiers

##### allow_destroy

Use `allow_destroy` to assert that the `:allow_destroy` option was specified.

class Car < ActiveRecord::Base
  accepts_nested_attributes_for :mirrors, allow_destroy: true
end

# RSpec
RSpec.describe Car, type: :model do
  it do
    should accept_nested_attributes_for(:mirrors).
      allow_destroy(true)
  end
end

# Minitest (Shoulda)
class CarTest < ActiveSupport::TestCase
  should accept_nested_attributes_for(:mirrors).
    allow_destroy(true)
end

##### limit

Use `limit` to assert that the `:limit` option was specified.

class Car < ActiveRecord::Base
  accepts_nested_attributes_for :windows, limit: 3
end

# RSpec
RSpec.describe Car, type: :model do
  it do
    should accept_nested_attributes_for(:windows).
      limit(3)
  end
end

# Minitest (Shoulda)
class CarTest < ActiveSupport::TestCase
  should accept_nested_attributes_for(:windows).
    limit(3)
end

##### update_only

Use `update_only` to assert that the `:update_only` option was specified.

class Car < ActiveRecord::Base
  accepts_nested_attributes_for :engine, update_only: true
end

# RSpec
RSpec.describe Car, type: :model do
  it do
    should accept_nested_attributes_for(:engine).
      update_only(true)
  end
end

# Minitest (Shoulda)
class CarTest < ActiveSupport::TestCase
  should accept_nested_attributes_for(:engine).
    update_only(true)
end

@return [AcceptNestedAttributesForMatcher]

# File lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb, line 93
def accept_nested_attributes_for(name)
  AcceptNestedAttributesForMatcher.new(name)
end
belong_to(name) click to toggle source

The `belong_to` matcher is used to ensure that a `belong_to` association exists on your model.

class Person < ActiveRecord::Base
  belongs_to :organization
end

# RSpec
RSpec.describe Person, type: :model do
  it { should belong_to(:organization) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should belong_to(:organization)
end

Note that polymorphic associations are automatically detected and do not need any qualifiers:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

# RSpec
RSpec.describe Comment, type: :model do
  it { should belong_to(:commentable) }
end

# Minitest (Shoulda)
class CommentTest < ActiveSupport::TestCase
  should belong_to(:commentable)
end

#### Qualifiers

##### conditions

Use `conditions` if your association is defined with a scope that sets the `where` clause.

class Person < ActiveRecord::Base
  belongs_to :family, -> { where(everyone_is_perfect: false) }
end

# RSpec
RSpec.describe Person, type: :model do
  it do
    should belong_to(:family).
      conditions(everyone_is_perfect: false)
  end
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should belong_to(:family).
    conditions(everyone_is_perfect: false)
end

##### order

Use `order` if your association is defined with a scope that sets the `order` clause.

class Person < ActiveRecord::Base
  belongs_to :previous_company, -> { order('hired_on desc') }
end

# RSpec
RSpec.describe Person, type: :model do
  it { should belong_to(:previous_company).order('hired_on desc') }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should belong_to(:previous_company).order('hired_on desc')
end

##### class_name

Use `class_name` to test usage of the `:class_name` option. This asserts that the model you're referring to actually exists.

class Person < ActiveRecord::Base
  belongs_to :ancient_city, class_name: 'City'
end

# RSpec
RSpec.describe Person, type: :model do
  it { should belong_to(:ancient_city).class_name('City') }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should belong_to(:ancient_city).class_name('City')
end

##### with_primary_key

Use `with_primary_key` to test usage of the `:primary_key` option.

class Person < ActiveRecord::Base
  belongs_to :great_country, primary_key: 'country_id'
end

# RSpec
RSpec.describe Person, type: :model do
  it do
    should belong_to(:great_country).
      with_primary_key('country_id')
  end
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should belong_to(:great_country).
    with_primary_key('country_id')
end

##### with_foreign_key

Use `with_foreign_key` to test usage of the `:foreign_key` option.

class Person < ActiveRecord::Base
  belongs_to :great_country, foreign_key: 'country_id'
end

# RSpec
RSpec.describe Person, type: :model do
  it do
    should belong_to(:great_country).
      with_foreign_key('country_id')
  end
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should belong_to(:great_country).
    with_foreign_key('country_id')
end

##### dependent

Use `dependent` to assert that the `:dependent` option was specified.

class Person < ActiveRecord::Base
  belongs_to :world, dependent: :destroy
end

# RSpec
RSpec.describe Person, type: :model do
  it { should belong_to(:world).dependent(:destroy) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should belong_to(:world).dependent(:destroy)
end

To assert that any `:dependent` option was specified, use `true`:

# RSpec
RSpec.describe Person, type: :model do
  it { should belong_to(:world).dependent(true) }
end

To assert that no `:dependent` option was specified, use `false`:

class Person < ActiveRecord::Base
  belongs_to :company
end

# RSpec
RSpec.describe Person, type: :model do
  it { should belong_to(:company).dependent(false) }
end

##### counter_cache

Use `counter_cache` to assert that the `:counter_cache` option was specified.

class Person < ActiveRecord::Base
  belongs_to :organization, counter_cache: true
end

# RSpec
RSpec.describe Person, type: :model do
  it { should belong_to(:organization).counter_cache(true) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should belong_to(:organization).counter_cache(true)
end

##### touch

Use `touch` to assert that the `:touch` option was specified.

class Person < ActiveRecord::Base
  belongs_to :organization, touch: true
end

# RSpec
RSpec.describe Person, type: :model do
  it { should belong_to(:organization).touch(true) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should belong_to(:organization).touch(true)
end

#### autosave

Use `autosave` to assert that the `:autosave` option was specified.

class Account < ActiveRecord::Base
  belongs_to :bank, autosave: true
end

# RSpec
RSpec.describe Account, type: :model do
  it { should belong_to(:bank).autosave(true) }
end

# Minitest (Shoulda)
class AccountTest < ActiveSupport::TestCase
  should belong_to(:bank).autosave(true)
end

##### inverse_of

Use `inverse_of` to assert that the `:inverse_of` option was specified.

class Person < ActiveRecord::Base
  belongs_to :organization, inverse_of: :employees
end

# RSpec
describe Person
  it { should belong_to(:organization).inverse_of(:employees) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should belong_to(:organization).inverse_of(:employees)
end

@return [AssociationMatcher]

# File lib/shoulda/matchers/active_record/association_matcher.rb, line 258
def belong_to(name)
  AssociationMatcher.new(:belongs_to, name)
end
define_enum_for(attribute_name) click to toggle source

The `define_enum_for` matcher is used to test that the `enum` macro has been used to decorate an attribute with enum methods.

class Process < ActiveRecord::Base
  enum status: [:running, :stopped, :suspended]
end

# RSpec
RSpec.describe Process, type: :model do
  it { should define_enum_for(:status) }
end

# Minitest (Shoulda)
class ProcessTest < ActiveSupport::TestCase
  should define_enum_for(:status)
end

#### Qualifiers

##### with

Use `with` to test that the enum has been defined with a certain set of known values.

class Process < ActiveRecord::Base
  enum status: [:running, :stopped, :suspended]
end

# RSpec
RSpec.describe Process, type: :model do
  it do
    should define_enum_for(:status).
      with([:running, :stopped, :suspended])
  end
end

# Minitest (Shoulda)
class ProcessTest < ActiveSupport::TestCase
  should define_enum_for(:status).
    with([:running, :stopped, :suspended])
end

@return [DefineEnumForMatcher]

# File lib/shoulda/matchers/active_record/define_enum_for_matcher.rb, line 48
def define_enum_for(attribute_name)
  DefineEnumForMatcher.new(attribute_name)
end
have_and_belong_to_many(name) click to toggle source

The `have_and_belong_to_many` matcher is used to test that a `has_and_belongs_to_many` association exists on your model and that the join table exists in the database.

class Person < ActiveRecord::Base
  has_and_belongs_to_many :awards
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_and_belong_to_many(:awards) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_and_belong_to_many(:awards)
end

#### Qualifiers

##### conditions

Use `conditions` if your association is defined with a scope that sets the `where` clause.

class Person < ActiveRecord::Base
  has_and_belongs_to_many :issues, -> { where(difficulty: 'hard') }
end

# RSpec
RSpec.describe Person, type: :model do
  it do
    should have_and_belong_to_many(:issues).
      conditions(difficulty: 'hard')
  end
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_and_belong_to_many(:issues).
    conditions(difficulty: 'hard')
end

##### order

Use `order` if your association is defined with a scope that sets the `order` clause.

class Person < ActiveRecord::Base
  has_and_belongs_to_many :projects, -> { order('time_spent') }
end

# RSpec
RSpec.describe Person, type: :model do
  it do
    should have_and_belong_to_many(:projects).
      order('time_spent')
  end
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_and_belong_to_many(:projects).
    order('time_spent')
end

##### class_name

Use `class_name` to test usage of the `:class_name` option. This asserts that the model you're referring to actually exists.

class Person < ActiveRecord::Base
  has_and_belongs_to_many :places_visited, class_name: 'City'
end

# RSpec
RSpec.describe Person, type: :model do
  it do
    should have_and_belong_to_many(:places_visited).
      class_name('City')
  end
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_and_belong_to_many(:places_visited).
    class_name('City')
end

##### join_table

Use `join_table` to test usage of the `:join_table` option. This asserts that the table you're referring to actually exists.

class Person < ActiveRecord::Base
  has_and_belongs_to_many :issues, join_table: 'people_tickets'
end

# RSpec
RSpec.describe Person, type: :model do
  it do
    should have_and_belong_to_many(:issues).
      join_table('people_tickets')
  end
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_and_belong_to_many(:issues).
    join_table('people_tickets')
end

##### validate

Use `validate` to test that the `:validate` option was specified.

class Person < ActiveRecord::Base
  has_and_belongs_to_many :interviews, validate: false
end

# RSpec
RSpec.describe Person, type: :model do
  it do
    should have_and_belong_to_many(:interviews).
      validate(false)
  end
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_and_belong_to_many(:interviews).
    validate(false)
end

#### autosave

Use `autosave` to assert that the `:autosave` option was specified.

class Publisher < ActiveRecord::Base
  has_and_belongs_to_many :advertisers, autosave: true
end

# RSpec
RSpec.describe Publisher, type: :model do
  it { should have_and_belong_to_many(:advertisers).autosave(true) }
end

# Minitest (Shoulda)
class AccountTest < ActiveSupport::TestCase
  should have_and_belong_to_many(:advertisers).autosave(true)
end

@return [AssociationMatcher]

# File lib/shoulda/matchers/active_record/association_matcher.rb, line 877
def have_and_belong_to_many(name)
  AssociationMatcher.new(:has_and_belongs_to_many, name)
end
have_db_column(column) click to toggle source

The `have_db_column` matcher tests that the table that backs your model has a specific column.

class CreatePhones < ActiveRecord::Migration
  def change
    create_table :phones do |t|
      t.string :supported_ios_version
    end
  end
end

# RSpec
RSpec.describe Phone, type: :model do
  it { should have_db_column(:supported_ios_version) }
end

# Minitest (Shoulda)
class PhoneTest < ActiveSupport::TestCase
  should have_db_column(:supported_ios_version)
end

#### Qualifiers

##### of_type

Use `of_type` to assert that a column is defined as a certain type.

class CreatePhones < ActiveRecord::Migration
  def change
    create_table :phones do |t|
      t.decimal :camera_aperture
    end
  end
end

# RSpec
RSpec.describe Phone, type: :model do
  it do
    should have_db_column(:camera_aperture).of_type(:decimal)
  end
end

# Minitest (Shoulda)
class PhoneTest < ActiveSupport::TestCase
  should have_db_column(:camera_aperture).of_type(:decimal)
end

##### with_options

Use `with_options` to assert that a column has been defined with certain options (`:precision`, `:limit`, `:default`, `:null`, `:scale`, or `:primary`).

class CreatePhones < ActiveRecord::Migration
  def change
    create_table :phones do |t|
      t.decimal :camera_aperture, precision: 1, null: false
    end
  end
end

# RSpec
RSpec.describe Phone, type: :model do
  it do
    should have_db_column(:camera_aperture).
      with_options(precision: 1, null: false)
  end
end

# Minitest (Shoulda)
class PhoneTest < ActiveSupport::TestCase
  should have_db_column(:camera_aperture).
    with_options(precision: 1, null: false)
end

@return [HaveDbColumnMatcher]

# File lib/shoulda/matchers/active_record/have_db_column_matcher.rb, line 81
def have_db_column(column)
  HaveDbColumnMatcher.new(column)
end
have_db_index(columns) click to toggle source

The `have_db_index` matcher tests that the table that backs your model has a index on a specific column.

class CreateBlogs < ActiveRecord::Migration
  def change
    create_table :blogs do |t|
      t.integer :user_id
    end

    add_index :blogs, :user_id
  end
end

# RSpec
RSpec.describe Blog, type: :model do
  it { should have_db_index(:user_id) }
end

# Minitest (Shoulda)
class BlogTest < ActiveSupport::TestCase
  should have_db_index(:user_id)
end

#### Qualifiers

##### unique

Use `unique` to assert that the index is unique.

class CreateBlogs < ActiveRecord::Migration
  def change
    create_table :blogs do |t|
      t.string :name
    end

    add_index :blogs, :name, unique: true
  end
end

# RSpec
RSpec.describe Blog, type: :model do
  it { should have_db_index(:name).unique(true) }
end

# Minitest (Shoulda)
class BlogTest < ActiveSupport::TestCase
  should have_db_index(:name).unique(true)
end

Since it only ever makes since for `unique` to be `true`, you can also leave off the argument to save some keystrokes:

# RSpec
RSpec.describe Blog, type: :model do
  it { should have_db_index(:name).unique }
end

# Minitest (Shoulda)
class BlogTest < ActiveSupport::TestCase
  should have_db_index(:name).unique
end

@return [HaveDbIndexMatcher]

# File lib/shoulda/matchers/active_record/have_db_index_matcher.rb, line 68
def have_db_index(columns)
  HaveDbIndexMatcher.new(columns)
end
have_many(name) click to toggle source

The `have_many` matcher is used to test that a `has_many` or `has_many :through` association exists on your model.

class Person < ActiveRecord::Base
  has_many :friends
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_many(:friends) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_many(:friends)
end

Note that polymorphic associations are automatically detected and do not need any qualifiers:

class Person < ActiveRecord::Base
  has_many :pictures, as: :imageable
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_many(:pictures) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_many(:pictures)
end

#### Qualifiers

##### conditions

Use `conditions` if your association is defined with a scope that sets the `where` clause.

class Person < ActiveRecord::Base
  has_many :coins, -> { where(quality: 'mint') }
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_many(:coins).conditions(quality: 'mint') }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_many(:coins).conditions(quality: 'mint')
end

##### order

Use `order` if your association is defined with a scope that sets the `order` clause.

class Person < ActiveRecord::Base
  has_many :shirts, -> { order('color') }
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_many(:shirts).order('color') }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_many(:shirts).order('color')
end

##### class_name

Use `class_name` to test usage of the `:class_name` option. This asserts that the model you're referring to actually exists.

class Person < ActiveRecord::Base
  has_many :hopes, class_name: 'Dream'
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_many(:hopes).class_name('Dream') }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_many(:hopes).class_name('Dream')
end

##### with_primary_key

Use `with_primary_key` to test usage of the `:primary_key` option.

class Person < ActiveRecord::Base
  has_many :worries, primary_key: 'worrier_id'
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_many(:worries).with_primary_key('worrier_id') }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_many(:worries).with_primary_key('worrier_id')
end

##### with_foreign_key

Use `with_foreign_key` to test usage of the `:foreign_key` option.

class Person < ActiveRecord::Base
  has_many :worries, foreign_key: 'worrier_id'
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_many(:worries).with_foreign_key('worrier_id') }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_many(:worries).with_foreign_key('worrier_id')
end

##### dependent

Use `dependent` to assert that the `:dependent` option was specified.

class Person < ActiveRecord::Base
  has_many :secret_documents, dependent: :destroy
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_many(:secret_documents).dependent(:destroy) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_many(:secret_documents).dependent(:destroy)
end

##### through

Use `through` to test usage of the `:through` option. This asserts that the association you are going through actually exists.

class Person < ActiveRecord::Base
  has_many :acquaintances, through: :friends
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_many(:acquaintances).through(:friends) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_many(:acquaintances).through(:friends)
end

##### source

Use `source` to test usage of the `:source` option on a `:through` association.

class Person < ActiveRecord::Base
  has_many :job_offers, through: :friends, source: :opportunities
end

# RSpec
RSpec.describe Person, type: :model do
  it do
    should have_many(:job_offers).
      through(:friends).
      source(:opportunities)
  end
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_many(:job_offers).
    through(:friends).
    source(:opportunities)
end

##### validate

Use `validate` to assert that the `:validate` option was specified.

class Person < ActiveRecord::Base
  has_many :ideas, validate: false
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_many(:ideas).validate(false) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_many(:ideas).validate(false)
end

#### autosave

Use `autosave` to assert that the `:autosave` option was specified.

class Player < ActiveRecord::Base
  has_many :games, autosave: true
end

# RSpec
RSpec.describe Player, type: :model do
  it { should have_many(:games).autosave(true) }
end

# Minitest (Shoulda)
class PlayerTest < ActiveSupport::TestCase
  should have_many(:games).autosave(true)
end

##### inverse_of

Use `inverse_of` to assert that the `:inverse_of` option was specified.

class Organization < ActiveRecord::Base
  has_many :employees, inverse_of: :company
end

# RSpec
describe Organization
  it { should have_many(:employees).inverse_of(:company) }
end

# Minitest (Shoulda)
class OrganizationTest < ActiveSupport::TestCase
  should have_many(:employees).inverse_of(:company)
end

@return [AssociationMatcher]

# File lib/shoulda/matchers/active_record/association_matcher.rb, line 509
def have_many(name)
  AssociationMatcher.new(:has_many, name)
end
have_one(name) click to toggle source

The `have_one` matcher is used to test that a `has_one` or `has_one :through` association exists on your model.

class Person < ActiveRecord::Base
  has_one :partner
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_one(:partner) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_one(:partner)
end

#### Qualifiers

##### conditions

Use `conditions` if your association is defined with a scope that sets the `where` clause.

class Person < ActiveRecord::Base
  has_one :pet, -> { where('weight < 80') }
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_one(:pet).conditions('weight < 80') }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_one(:pet).conditions('weight < 80')
end

##### order

Use `order` if your association is defined with a scope that sets the `order` clause.

class Person < ActiveRecord::Base
  has_one :focus, -> { order('priority desc') }
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_one(:focus).order('priority desc') }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_one(:focus).order('priority desc')
end

##### class_name

Use `class_name` to test usage of the `:class_name` option. This asserts that the model you're referring to actually exists.

class Person < ActiveRecord::Base
  has_one :chance, class_name: 'Opportunity'
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_one(:chance).class_name('Opportunity') }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_one(:chance).class_name('Opportunity')
end

##### dependent

Use `dependent` to test that the `:dependent` option was specified.

class Person < ActiveRecord::Base
  has_one :contract, dependent: :nullify
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_one(:contract).dependent(:nullify) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_one(:contract).dependent(:nullify)
end

##### with_primary_key

Use `with_primary_key` to test usage of the `:primary_key` option.

class Person < ActiveRecord::Base
  has_one :job, primary_key: 'worker_id'
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_one(:job).with_primary_key('worker_id') }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_one(:job).with_primary_key('worker_id')
end

##### with_foreign_key

Use `with_foreign_key` to test usage of the `:foreign_key` option.

class Person < ActiveRecord::Base
  has_one :job, foreign_key: 'worker_id'
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_one(:job).with_foreign_key('worker_id') }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_one(:job).with_foreign_key('worker_id')
end

##### through

Use `through` to test usage of the `:through` option. This asserts that the association you are going through actually exists.

class Person < ActiveRecord::Base
  has_one :life, through: :partner
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_one(:life).through(:partner) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_one(:life).through(:partner)
end

##### source

Use `source` to test usage of the `:source` option on a `:through` association.

class Person < ActiveRecord::Base
  has_one :car, through: :partner, source: :vehicle
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_one(:car).through(:partner).source(:vehicle) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_one(:car).through(:partner).source(:vehicle)
end

##### validate

Use `validate` to assert that the the `:validate` option was specified.

class Person < ActiveRecord::Base
  has_one :parking_card, validate: false
end

# RSpec
RSpec.describe Person, type: :model do
  it { should have_one(:parking_card).validate(false) }
end

# Minitest (Shoulda)
class PersonTest < ActiveSupport::TestCase
  should have_one(:parking_card).validate(false)
end

#### autosave

Use `autosave` to assert that the `:autosave` option was specified.

class Account < ActiveRecord::Base
  has_one :bank, autosave: true
end

# RSpec
RSpec.describe Account, type: :model do
  it { should have_one(:bank).autosave(true) }
end

# Minitest (Shoulda)
class AccountTest < ActiveSupport::TestCase
  should have_one(:bank).autosave(true)
end

@return [AssociationMatcher]

# File lib/shoulda/matchers/active_record/association_matcher.rb, line 719
def have_one(name)
  AssociationMatcher.new(:has_one, name)
end
have_readonly_attribute(value) click to toggle source

The `have_readonly_attribute` matcher tests usage of the `attr_readonly` macro.

class User < ActiveRecord::Base
  attr_readonly :password
end

# RSpec
RSpec.describe User, type: :model do
  it { should have_readonly_attribute(:password) }
end

# Minitest (Shoulda)
class UserTest < ActiveSupport::TestCase
  should have_readonly_attribute(:password)
end

@return [HaveReadonlyAttributeMatcher]

# File lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb, line 23
def have_readonly_attribute(value)
  HaveReadonlyAttributeMatcher.new(value)
end
serialize(name) click to toggle source

The `serialize` matcher tests usage of the `serialize` macro.

class Product < ActiveRecord::Base
  serialize :customizations
end

# RSpec
RSpec.describe Product, type: :model do
  it { should serialize(:customizations) }
end

# Minitest (Shoulda)
class ProductTest < ActiveSupport::TestCase
  should serialize(:customizations)
end

#### Qualifiers

##### as

Use `as` if you are using a custom serializer class.

class ProductSpecsSerializer
  def load(string)
    # ...
  end

  def dump(options)
    # ...
  end
end

class Product < ActiveRecord::Base
  serialize :specifications, ProductSpecsSerializer
end

# RSpec
RSpec.describe Product, type: :model do
  it do
    should serialize(:specifications).
      as(ProductSpecsSerializer)
  end
end

# Minitest (Shoulda)
class ProductTest < ActiveSupport::TestCase
  should serialize(:specifications).
    as(ProductSpecsSerializer)
end

##### as_instance_of

Use `as_instance_of` if you are using a custom serializer object.

class ProductOptionsSerializer
  def load(string)
    # ...
  end

  def dump(options)
    # ...
  end
end

class Product < ActiveRecord::Base
  serialize :options, ProductOptionsSerializer.new
end

# RSpec
RSpec.describe Product, type: :model do
  it do
    should serialize(:options).
      as_instance_of(ProductOptionsSerializer)
  end
end

# Minitest (Shoulda)
class ProductTest < ActiveSupport::TestCase
  should serialize(:options).
    as_instance_of(ProductOptionsSerializer)
end

@return [SerializeMatcher]

# File lib/shoulda/matchers/active_record/serialize_matcher.rb, line 88
def serialize(name)
  SerializeMatcher.new(name)
end
validate_uniqueness_of(attr) click to toggle source

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 instance of that model 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
RSpec.describe Post, type: :model do
  it { should validate_uniqueness_of(:permalink) }
end

# Minitest (Shoulda)
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 populate these attributes with values 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:

RSpec.describe Post, type: :model do
  it { should validate_uniqueness_of(:title) }
end

However, running this test will fail with an exception such as:

Shoulda::Matchers::ActiveRecord::ValidateUniquenessOfMatcher::ExistingRecordInvalid:
  validate_uniqueness_of works by matching a new record against an
  existing record. If there is no existing record, it will create one
  using the record you provide.

  While doing this, the following error was raised:

    PG::NotNullViolation: ERROR:  null value in column "content" violates not-null constraint
    DETAIL:  Failing row contains (1, null, null).
    : INSERT INTO "posts" DEFAULT VALUES RETURNING "id"

  The best way to fix this is to provide the matcher with a record where
  any required attributes are filled in with valid values beforehand.

(The exact error message will differ depending on which database you're using, but you get the idea.)

This happens because `validate_uniqueness_of` tries to create a new post but cannot do so because of the `content` attribute: though unrelated to this test, it nevertheless needs to be filled in. As indicated at the end of the error message, the solution is to build a custom Post object ahead of time with `content` filled in:

RSpec.describe Post, type: :model do
  describe "validations" do
    subject { Post.new(content: "Here is the content") }
    it { should validate_uniqueness_of(:title) }
  end
end

Or, if you're using [FactoryGirl](github.com/thoughtbot/factory_girl) and you have a `post` factory defined which automatically fills in `content`, you can say:

RSpec.describe Post, type: :model do
  describe "validations" do
    subject { FactoryGirl.build(:post) }
    it { should validate_uniqueness_of(:title) }
  end
end

#### Qualifiers

Use `on` if your validation applies only under a certain context.

class Post < ActiveRecord::Base
  validates_uniqueness_of :title, on: :create
end

# RSpec
RSpec.describe Post, type: :model do
  it { should validate_uniqueness_of(:title).on(:create) }
end

# Minitest (Shoulda)
class PostTest < ActiveSupport::TestCase
  should validate_uniqueness_of(:title).on(:create)
end

##### 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
RSpec.describe Post, type: :model do
  it do
    should validate_uniqueness_of(:title).
      with_message('Please choose another title')
  end
end

# Minitest (Shoulda)
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: :journal_id
end

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

# Minitest (Shoulda)
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
RSpec.describe Post, type: :model do
  it { should validate_uniqueness_of(:key).case_insensitive }
end

# Minitest (Shoulda)
class PostTest < ActiveSupport::TestCase
  should validate_uniqueness_of(:key).case_insensitive
end

##### ignoring_case_sensitivity

By default, `validate_uniqueness_of` will check that the validation is case sensitive: it asserts that uniquable attributes pass validation when their values are in a different case than corresponding attributes in the pre-existing record.

Use `ignoring_case_sensitivity` to skip this check. This qualifier is particularly handy if your model has somehow changed the behavior of attribute you're testing so that it modifies the case of incoming values as they are set. For instance, perhaps you've overridden the writer method or added a `before_validation` callback to normalize the attribute.

class User < ActiveRecord::Base
  validates_uniqueness_of :email

  def email=(value)
    super(value.downcase)
  end
end

# RSpec
RSpec.describe Post, type: :model do
  it do
    should validate_uniqueness_of(:email).ignoring_case_sensitivity
  end
end

# Minitest (Shoulda)
class PostTest < ActiveSupport::TestCase
  should validate_uniqueness_of(:email).ignoring_case_sensitivity
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
RSpec.describe Post, type: :model do
  it { should validate_uniqueness_of(:author_id).allow_nil }
end

# Minitest (Shoulda)
class PostTest < ActiveSupport::TestCase
  should validate_uniqueness_of(:author_id).allow_nil
end

@return [ValidateUniquenessOfMatcher]

##### allow_blank

Use `allow_blank` to assert that the attribute allows a blank value.

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

# RSpec
RSpec.describe Post, type: :model do
  it { should validate_uniqueness_of(:author_id).allow_blank }
end

# Minitest (Shoulda)
class PostTest < ActiveSupport::TestCase
  should validate_uniqueness_of(:author_id).allow_blank
end

@return [ValidateUniquenessOfMatcher]

# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 255
def validate_uniqueness_of(attr)
  ValidateUniquenessOfMatcher.new(attr)
end