module Shoulda::Matchers::ActiveRecord
This module provides matchers that are used to test behavior within ActiveRecord
classes.
Public Instance Methods
Source
# File lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb, line 93 def accept_nested_attributes_for(name) AcceptNestedAttributesForMatcher.new(name) end
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]
Source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 325 def belong_to(name) AssociationMatcher.new(:belongs_to, name) end
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
##### required
Use ‘required` to assert that the association is not allowed to be nil. (Enabled by default in Rails 5+.)
class Person < ActiveRecord::Base belongs_to :organization, required: true end # RSpec describe Person it { should belong_to(:organization).required } end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should belong_to(:organization).required end
##### without_validating_presence
Use ‘without_validating_presence` with `belong_to` to prevent the matcher from checking whether the association disallows nil (Rails 5+ only). This can be helpful if you have a custom hook that always sets the association to a meaningful value:
class Person < ActiveRecord::Base belongs_to :organization before_validation :autoassign_organization private def autoassign_organization self.organization = Organization.create! end end # RSpec describe Person it { should belong_to(:organization).without_validating_presence } end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should belong_to(:organization).without_validating_presence end
##### optional
Use ‘optional` to assert that the association is allowed to be nil. (Rails 5+ only.)
class Person < ActiveRecord::Base belongs_to :organization, optional: true end # RSpec describe Person it { should belong_to(:organization).optional } end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should belong_to(:organization).optional end
@return [AssociationMatcher]
Source
# File lib/shoulda/matchers/active_record/define_enum_for_matcher.rb, line 168 def define_enum_for(attribute_name) DefineEnumForMatcher.new(attribute_name) end
The ‘define_enum_for` matcher is used to test that the `enum` macro has been used to decorate an attribute with enum capabilities.
class Process < ActiveRecord::Base enum status: [:running, :stopped, :suspended] alias_attribute :kind, :SomeLegacyField enum kind: [:foo, :bar] end # RSpec RSpec.describe Process, type: :model do it { should define_enum_for(:status) } it { should define_enum_for(:kind) } end # Minitest (Shoulda) class ProcessTest < ActiveSupport::TestCase should define_enum_for(:status) should define_enum_for(:kind) end
#### Qualifiers
##### with_values
Use ‘with_values` to test that the attribute can only receive a certain set of possible 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_values([:running, :stopped, :suspended]) end end # Minitest (Shoulda) class ProcessTest < ActiveSupport::TestCase should define_enum_for(:status). with_values([:running, :stopped, :suspended]) end
If the values backing your enum attribute are arbitrary instead of a series of integers starting from 0, pass a hash to ‘with_values` instead of an array:
class Process < ActiveRecord::Base enum status: { running: 0, stopped: 1, suspended: 3, other: 99 } end # RSpec RSpec.describe Process, type: :model do it do should define_enum_for(:status). with_values(running: 0, stopped: 1, suspended: 3, other: 99) end end # Minitest (Shoulda) class ProcessTest < ActiveSupport::TestCase should define_enum_for(:status). with_values(running: 0, stopped: 1, suspended: 3, other: 99) end
##### backed_by_column_of_type
Use ‘backed_by_column_of_type` when the column backing your column type is a string instead of an integer:
class LoanApplication < ActiveRecord::Base enum status: { active: "active", pending: "pending", rejected: "rejected" } end # RSpec RSpec.describe LoanApplication, type: :model do it do should define_enum_for(:status). with_values( active: "active", pending: "pending", rejected: "rejected" ). backed_by_column_of_type(:string) end end # Minitest (Shoulda) class LoanApplicationTest < ActiveSupport::TestCase should define_enum_for(:status). with_values( active: "active", pending: "pending", rejected: "rejected" ). backed_by_column_of_type(:string) end ##### with_prefix
Use ‘with_prefix` to test that the enum is defined with a `_prefix` option (Rails 5 only). Can take either a boolean or a symbol:
class Issue < ActiveRecord::Base enum status: [:open, :closed], _prefix: :old end # RSpec RSpec.describe Issue, type: :model do it do should define_enum_for(:status). with_values([:open, :closed]). with_prefix(:old) end end # Minitest (Shoulda) class ProcessTest < ActiveSupport::TestCase should define_enum_for(:status). with_values([:open, :closed]). with_prefix(:old) end
##### with_suffix
Use ‘with_suffix` to test that the enum is defined with a `_suffix` option (Rails 5 only). Can take either a boolean or a symbol:
class Issue < ActiveRecord::Base enum status: [:open, :closed], _suffix: true end # RSpec RSpec.describe Issue, type: :model do it do should define_enum_for(:status). with_values([:open, :closed]). with_suffix end end # Minitest (Shoulda) class ProcessTest < ActiveSupport::TestCase should define_enum_for(:status). with_values([:open, :closed]). with_suffix end
@return [DefineEnumForMatcher]
Source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 982 def have_and_belong_to_many(name) AssociationMatcher.new(:has_and_belongs_to_many, name) end
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]
Source
# File lib/shoulda/matchers/active_record/have_db_column_matcher.rb, line 81 def have_db_column(column) HaveDbColumnMatcher.new(column) end
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`, `:primary` or `:array`).
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]
Source
# File lib/shoulda/matchers/active_record/have_db_index_matcher.rb, line 110 def have_db_index(columns) HaveDbIndexMatcher.new(columns) end
The ‘have_db_index` matcher tests that the table that backs your model has a specific index.
You can specify one 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
Or you can specify a group of columns:
class CreateBlogs < ActiveRecord::Migration def change create_table :blogs do |t| t.integer :user_id t.string :name end add_index :blogs, :user_id, :name end end # RSpec RSpec.describe Blog, type: :model do it { should have_db_index([:user_id, :name]) } end # Minitest (Shoulda) class BlogTest < ActiveSupport::TestCase should have_db_index([:user_id, :name]) end
Finally, if you’re using Rails 5 and PostgreSQL, you can also specify an expression:
class CreateLoggedErrors < ActiveRecord::Migration def change create_table :logged_errors do |t| t.string :code t.jsonb :content end add_index :logged_errors, 'lower(code)::text' end end # RSpec RSpec.describe LoggedError, type: :model do it { should have_db_index('lower(code)::text') } end # Minitest (Shoulda) class LoggedErrorTest < ActiveSupport::TestCase should have_db_index('lower(code)::text') end
#### Qualifiers
##### unique
Use ‘unique` to assert that the index is either unique or non-unique:
class CreateBlogs < ActiveRecord::Migration def change create_table :blogs do |t| t.string :domain t.integer :user_id end add_index :blogs, :domain, unique: true add_index :blogs, :user_id end end # RSpec RSpec.describe Blog, type: :model do it { should have_db_index(:name).unique } it { should have_db_index(:name).unique(true) } # if you want to be explicit it { should have_db_index(:user_id).unique(false) } end # Minitest (Shoulda) class BlogTest < ActiveSupport::TestCase should have_db_index(:name).unique should have_db_index(:name).unique(true) # if you want to be explicit should have_db_index(:user_id).unique(false) end
@return [HaveDbIndexMatcher]
Source
# File lib/shoulda/matchers/active_record/have_implicit_order_column.rb, line 24 def have_implicit_order_column(column_name) HaveImplicitOrderColumnMatcher.new(column_name) end
Source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 595 def have_many(name) AssociationMatcher.new(:has_many, name) end
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
##### index_errors
Use ‘index_errors` to assert that the `:index_errors` option was specified.
class Player < ActiveRecord::Base has_many :games, index_errors: true end # RSpec RSpec.describe Player, type: :model do it { should have_many(:games).index_errors(true) } end # Minitest (Shoulda) class PlayerTest < ActiveSupport::TestCase should have_many(:games).index_errors(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]
Source
# File lib/shoulda/matchers/active_record/have_attached_matcher.rb, line 50 def have_many_attached(name) HaveAttachedMatcher.new(:many, name) end
The ‘have_many_attached` matcher tests usage of the `has_many_attached` macro.
#### Example
class Message < ApplicationRecord has_many_attached :images end # RSpec RSpec.describe Message, type: :model do it { should have_many_attached(:images) } end # Minitest (Shoulda) class MessageTest < ActiveSupport::TestCase should have_many_attached(:images) end
@return [HaveAttachedMatcher]
Source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 824 def have_one(name) AssociationMatcher.new(:has_one, name) end
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
##### required
Use ‘required` to assert that the association is not allowed to be nil. (Rails 5+ only.)
class Person < ActiveRecord::Base has_one :brain, required: true end # RSpec describe Person it { should have_one(:brain).required } end # Minitest (Shoulda) class PersonTest < ActiveSupport::TestCase should have_one(:brain).required end
@return [AssociationMatcher]
Source
# File lib/shoulda/matchers/active_record/have_attached_matcher.rb, line 25 def have_one_attached(name) HaveAttachedMatcher.new(:one, name) end
The ‘have_one_attached` matcher tests usage of the `has_one_attached` macro.
#### Example
class User < ApplicationRecord has_one_attached :avatar end # RSpec RSpec.describe User, type: :model do it { should have_one_attached(:avatar) } end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should have_one_attached(:avatar) end
@return [HaveAttachedMatcher]
Source
# File lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb, line 23 def have_readonly_attribute(value) HaveReadonlyAttributeMatcher.new(value) end
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]
Source
# File lib/shoulda/matchers/active_record/have_rich_text_matcher.rb, line 25 def have_rich_text(rich_text_attribute) HaveRichTextMatcher.new(rich_text_attribute) end
The ‘have_rich_text` matcher tests usage of the `has_rich_text` macro.
#### Example
class Post < ActiveRecord has_rich_text :content end # RSpec RSpec.describe Post, type: :model do it { should have_rich_text(:content) } end # Minitest (Shoulda) class PostTest < ActiveSupport::TestCase should have_rich_text(:content) end
@return [HaveRichTextMatcher]
Source
# File lib/shoulda/matchers/active_record/have_secure_token_matcher.rb, line 47 def have_secure_token(token_attribute = :token) HaveSecureTokenMatcher.new(token_attribute) end
The ‘have_secure_token` matcher tests usage of the `has_secure_token` macro.
class User < ActiveRecord has_secure_token has_secure_token :auth_token end # RSpec RSpec.describe User, type: :model do it { should have_secure_token } it { should have_secure_token(:auth_token) } end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should have_secure_token should have_secure_token(:auth_token) end
#### Qualifiers
##### ignoring_check_for_db_index
By default, this matcher tests that an index is defined on your token column. Use ‘ignoring_check_for_db_index` if this is not the case.
class User < ActiveRecord has_secure_token :auth_token end # RSpec RSpec.describe User, type: :model do it { should have_secure_token(:auth_token).ignoring_check_for_db_index } end # Minitest (Shoulda) class UserTest < ActiveSupport::TestCase should have_secure_token(:auth_token).ignoring_check_for_db_index end
@return [HaveSecureToken]
Source
# File lib/shoulda/matchers/active_record/serialize_matcher.rb, line 88 def serialize(name) SerializeMatcher.new(name) end
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]
Source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 261 def validate_uniqueness_of(attr) ValidateUniquenessOfMatcher.new(attr) end
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 :permalink, uniqueness: true 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 [FactoryBot](github.com/thoughtbot/factory_bot) 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 { FactoryBot.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 :title, uniqueness: true, 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 :title, uniqueness: true, 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 :slug, uniqueness: { 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
NOTE: Support for testing uniqueness validation scoped to an array of associations is not available.
For more information, please refer to github.com/thoughtbot/shoulda-matchers/issues/814
##### 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 :key, uniqueness: { 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 :email, uniqueness: true 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 :author_id, uniqueness: true, 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 :author_id, uniqueness: true, 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]