SmartRspec
It's time to make your specs even more awesome! SmartRspec adds useful macros and matchers into the RSpec's test suite, so you can quickly define specs for your Rails app and get focused on making things turn into green.
Installation
Compatible with:
- Ruby 1.9+
- ActiveRecord (model macros)
Add this line to your application's Gemfile:
gem 'smart_rspec'
Execute:
$ bundle
Require the gem at the top of your spec/rails_helper.rb
(or equivalent):
require 'smart_rspec'
Then include the SmartRspec module:
RSpec.configure do |config|
config.include SmartRspec
end
Usage
- Macros
- has_attributes
- belongs_to, has_one, has_many
- fails_validation_of
- Matchers
- "Be" matchers
- be_boolean
- be_email
- be_url
- be_image_url
- be_a_list_of
- be_ascending
- be_descending
- "Have" matchers
- have
- have_at_least
- have_at_most
- have_error_on
- Other matchers
- include_items
- "Be" matchers
Macros
You will just need to define a valid subject
to start using SmartRspec's macros in your spec file.
has_attributes
It builds specs for model attributes and test its type, enumerated values and defaults:
RSpec.describe User, type: :model do
subject { FactoryGirl.build(:user) }
has_attributes :email, type: :String
has_attributes :is_admin, type: :Boolean
has_attributes :score, type: :Integer, default: 0
has_attributes :locale, type: :String, enum: %i(en pt), default: 'en'
end
belongs_to, has_one, has_many
It builds specs and test model associations like belongs_to
, has_one
and has_many
.
RSpec.describe User, type: :model do
subject { FactoryGirl.build(:user) }
belongs_to :business
has_one :project
has_many :tasks
end
fails_validation_of
It builds specs and forces model validations to fail, meaning that you will only turn specs into green when you specify the corresponding validation in the model. In order to get a nice semantics it's recommended to use the fails_validation_of
macro within a "when invalid" context, like:
RSpec.describe User, type: :model do
subject { FactoryGirl.build(:user) }
context 'when invalid' do
fails_validation_of :email, presence: true, email: true
fails_validation_of :name, length: { maximum: 80 }, uniqueness: true
fails_validation_of :username, length: { minimum: 10 }, exclusion: { in: %w(foo bar) }
# Other validations...
end
end
The fails_validation_of
implements specs for the following validations:
presence
email
length
exclusion
inclusion
uniqueness
format
In two cases it will require a valid mock to be passed so SmartRspec can use it to force the validation to fail properly.
For uniqueness with scope:
other_user = FactoryGirl.build(:other_valid_user)
fails_validation_of :username, uniqueness: { scope: :name, mock: other_user }
For format:
fails_validation_of :foo, format: { with: /foo/, mock: 'bar' }
Matchers
SmartRspec gathers a collection of custom useful matchers:
Be matchers
be_boolean
it { expect(true).to be_boolean }
it { expect('true').not_to be_boolean }
be_email
it { expect('tiagopog@gmail.com').to be_email }
it { expect('tiagopog@gmail').not_to be_email }
be_url
it { expect('http://adtangerine.com').to be_url }
it { expect('adtangerine.com').not_to be_url }
be_image_url
it { expect('http://adtangerine.com/foobar.png').to be_image_url }
it { expect('http://adtangerine.com/foobar.jpg').not_to be_image_url(:gif) }
it { expect('http://adtangerine.com/foo/bar').not_to be_image_url }
be_a_list_of
it { expect(Foo.fetch_api).to be_a_list_of(Foo)) }
be_ascending
it { expect([1, 2, 3, 4]).to be_ascending }
it { expect([1, 4, 2, 3]).not_to be_ascending }
be_descending
it { expect([4, 3, 2, 1]).to be_descending }
it { expect([1, 2, 3, 4]).not_to be_descending }
Have matchers
have(x).items
it { expect([1]).to have(1).item }
it { expect(%w(foo bar)).to have(2).items }
it { expect(%w(foo bar)).not_to have(1).item }
have_at_least
it { expect(%w(foo bar foobar)).to have_at_least(3).items }
have_at_most
it { expect(%w(foo bar foobar)).to have_at_most(3).items }
have_error_on
subject { User.new(email: nil, name: Faker::Name.name) }
it 'has an invalid email' do
subject.valid?
is_expected.to have_error_on(:email)
end
Other matchers
include_items
Comparing to array:
it { expect(%w(foo bar foobar)).to include_items(%w(foo bar foobar)) }
Comparing to multiple arguments:
it 'includes all items' do
item1, item2 = 'foo', 'bar'
expect(%w(foo bar)).to include_items(item1, item2))
end
Credits
- Some of the "have" matchers (precisely
have
,have_at_least
andhave_at_most
) were taken from therspec-collection_matchers
gem. - Some of the macros/matchers were inspired in RSpec helpers that I worked along with two friends (Douglas André and Giovanni Bonetti) at the Beauty Date project.
TODO
- Create macros for model scopes;
- Create macros for controllers;
- Add more matchers;
- Take groups of matchers into modules;
- Turn the whole into "A" in Code Climate.
Contributing
- Fork it;
- Create your feature branch (
git checkout -b my-new-feature
); - Create your specs and make sure they are passing;
- Document your feature in the README.md;
- Commit your changes (
git commit -am 'Add some feature'
); - Push to the branch (
git push origin my-new-feature
); - Create new Pull Request.