SmartRspec

Build Status Code Climate Dependency Status Gem Version

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

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('[email protected]').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

  1. Some of the "have" matchers (precisely have, have_at_least and have_at_most) were taken from the rspec-collection_matchers gem.
  2. 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

  1. Fork it;
  2. Create your feature branch (git checkout -b my-new-feature);
  3. Create your specs and make sure they are passing;
  4. Document your feature in the README.md;
  5. Commit your changes (git commit -am 'Add some feature');
  6. Push to the branch (git push origin my-new-feature);
  7. Create new Pull Request.