mongoid-rspec
mongoid-rspec provides a collection of RSpec-compatible matchers that help to test Mongoid documents.
Installation
With Mongoid 5.x
Use mongoid-rspec 3.0.0
gem 'mongoid-rspec', '3.0.0'
With Mongoid 4.x
Use mongoid-rspec 2.1.0
gem 'mongoid-rspec', '~> 2.1.0'
With Mongoid 3.x
Use mongoid-rspec 1.13.0.
gem 'mongoid-rspec', '~> 1.13.0'
With Mongoid 2.x
Use mongoid-rspec 1.4.5
gem 'mongoid-rspec', '1.4.5'
Configuring
Drop in existing or dedicated support file in spec/support.
i.e: spec/support/mongoid.rb
RSpec.configure do |config|
config.include Mongoid::Matchers, type: :model
end
If you aren't using rails then you don't have to specify the type. If you want to know why visit the rspec documentation.
Matchers
Association Matchers
RSpec.describe User do
it { is_expected.to have_many(:articles).with_foreign_key(:author_id).ordered_by(:title) }
it { is_expected.to have_one(:record) }
#can verify autobuild is set to true
it { is_expected.to have_one(:record).with_autobuild }
it { is_expected.to have_many :comments }
#can also specify with_dependent to test if :dependent => :destroy/:destroy_all/:delete is set
it { is_expected.to have_many(:comments).with_dependent(:destroy) }
#can verify autosave is set to true
it { is_expected.to have_many(:comments).with_autosave }
it { is_expected.to :profile }
it { is_expected.to have_and_belong_to_many(:children) }
it { is_expected.to have_and_belong_to_many(:children).of_type(User) }
end
RSpec.describe Profile do
it { is_expected.to (:user).as_inverse_of(:profile) }
end
RSpec.describe Article do
it { is_expected.to belong_to(:author).of_type(User).as_inverse_of(:articles) }
it { is_expected.to belong_to(:author).of_type(User).as_inverse_of(:articles).with_index }
it { is_expected.to (:comments) }
end
RSpec.describe Comment do
it { is_expected.to (:article).as_inverse_of(:comments) }
it { is_expected.to belong_to(:user).as_inverse_of(:comments) }
end
RSpec.describe Record do
it { is_expected.to belong_to(:user).as_inverse_of(:record) }
end
RSpec.describe Site do
it { is_expected.to have_many(:users).as_inverse_of(:site).ordered_by(:email.asc).with_counter_cache }
end
Mass Assignment Matcher
RSpec.describe User do
it { is_expected.to allow_mass_assignment_of(:login) }
it { is_expected.to allow_mass_assignment_of(:email) }
it { is_expected.to allow_mass_assignment_of(:age) }
it { is_expected.to allow_mass_assignment_of(:password) }
it { is_expected.to allow_mass_assignment_of(:password) }
it { is_expected.to allow_mass_assignment_of(:role).as(:admin) }
it { is_expected.not_to allow_mass_assignment_of(:role) }
end
Validation Matchers
RSpec.describe Site do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name) }
end
RSpec.describe User do
it { is_expected.to validate_presence_of(:login) }
it { is_expected.to validate_uniqueness_of(:login).scoped_to(:site) }
it { is_expected.to validate_uniqueness_of(:email).case_insensitive.("is already taken") }
it { is_expected.to validate_format_of(:login).to_allow("valid_login").not_to_allow("invalid login") }
it { is_expected.to validate_associated(:profile) }
it { is_expected.to validate_exclusion_of(:login).to_not_allow("super", "index", "edit") }
it { is_expected.to validate_inclusion_of(:role).to_allow("admin", "member") }
it { is_expected.to validate_confirmation_of(:email) }
it { is_expected.to validate_presence_of(:age).on(:create, :update) }
it { is_expected.to validate_numericality_of(:age).on(:create, :update) }
it { is_expected.to validate_inclusion_of(:age).to_allow(23..42).on([:create, :update]) }
it { is_expected.to validate_presence_of(:password).on(:create) }
it { is_expected.to validate_presence_of(:provider_uid).on(:create) }
it { is_expected.to validate_inclusion_of(:locale).to_allow([:en, :ru]) }
end
RSpec.describe Article do
it { is_expected.to validate_length_of(:title).within(8..16) }
end
RSpec.describe Profile do
it { is_expected.to validate_numericality_of(:age).greater_than(0) }
end
RSpec.describe MovieArticle do
it { is_expected.to validate_numericality_of(:rating).to_allow(:greater_than => 0).less_than_or_equal_to(5) }
it { is_expected.to validate_numericality_of(:classification).to_allow(:even => true, :only_integer => true, :nil => false) }
end
RSpec.describe Person do
# in order to be able to use the custom_validate matcher, the custom validator class (in this case SsnValidator)
# should redefine the kind method to return :custom, i.e. "def self.kind() :custom end"
it { is_expected.to custom_validate(:ssn).with_validator(SsnValidator) }
end
Accepts Nested Attributes Matcher
RSpec.describe User do
it { is_expected.to accept_nested_attributes_for(:articles) }
it { is_expected.to accept_nested_attributes_for(:comments) }
end
RSpec.describe Article do
it { is_expected.to accept_nested_attributes_for(:permalink) }
end
Index Matcher
RSpec.describe Article do
it { is_expected.to have_index_for(published: 1) }
it { is_expected.to have_index_for(title: 1).(unique: true, background: true) }
end
RSpec.describe Profile do
it { is_expected.to have_index_for(first_name: 1, last_name: 1) }
end
Rspec.describe Log do
it { is_expected.to have_index_for(created_at: 1).(bucket_size: 100, expire_after_seconds: 3600) }
end
Others
RSpec.describe User do
it { is_expected.to have_fields(:email, :login) }
it { is_expected.to have_field(:s).with_alias(:status) }
it { is_expected.to have_fields(:birthdate, :registered_at).of_type(DateTime) }
# if you're declaring 'include Mongoid::Timestamps'
# or any of 'include Mongoid::Timestamps::Created' and 'Mongoid::Timestamps::Updated'
it { is_expected.to }
it { is_expected.to .with(:created) }
it { is_expected.not_to .with(:updated) }
it { is_expected.to be_versioned_document } # if you're declaring `include Mongoid::Versioning`
it { is_expected.to be_paranoid_document } # if you're declaring `include Mongoid::Paranoia`
it { is_expected.to be_multiparameted_document } # if you're declaring `include Mongoid::MultiParameterAttributes`
end
RSpec.describe Log do
it { is_expected.to be_stored_in :logs }
it { is_expected.to be_dynamic_document }
end
RSpec.describe Article do
it { is_expected.to have_field(:published).of_type(Boolean).with_default_value_of(false) }
it { is_expected.to have_field(:allow_comments).of_type(Boolean).with_default_value_of(true) }
it { is_expected.not_to have_field(:allow_comments).of_type(Boolean).with_default_value_of(false) }
it { is_expected.not_to have_field(:number_of_comments).of_type(Integer).with_default_value_of(1) }
end
Known issues
accept_nested_attributes_for matcher must test options issue 91.
Acknowledgement
Thanks to Durran Jordan for providing the changes necessary to make this compatible with mongoid 2.0.0.rc, and for other contributors to this project.