No release in over 3 years
Low commit activity in last 3 years
A standardized structure for request specs in Rails.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

RSpec::DeclarativeRequests

A standardized structure for request specs in Rails.

require 'rails_helper'

RSpec.describe 'GET /widgets/:id' do
  let(:id) { FactoryBot.create(:widget).id }
  it { is_expected.to have_http_status(:ok) }
end

# fancier
RSpec.describe 'GET /widgets/:widget#id' do
  let(:widget) { FactoryBot.create(:widget) }

  it "responds with the widget" do
    is_expected.to have_attributes(
      status: 200,
      content_type: 'application/json',
      body: be_json( # `be_json` sold separately (from `saharspec` or `rspec-composable_json_matchers`)
        widget: {
          id: 7,
          name: 'Kevin',
        }
      )
    )
  end
end

Setup

Add to your Gemfile (probably in the :test group):

# Gemfile
group :test do
  gem 'rspec-declarative_requests'
end

Include it in your RSpec config:

# spec/rails_helper.rb
RSpec.configure do |config|
  # enabled for ALL request specs
  config.include RSpec::DeclarativeRequests, type: :request

  # OR: enable only for request specs tagged as :declarative
  config.include RSpec::DeclarativeRequests, type: :request, declarative: true
end

Or include it straight into the spec:

# spec/requests/whatever_spec.rb
require 'rails_helper'

RSpec.describe 'Whatever' do
  include RSpec::DeclarativeRequests

  describe 'GET /whatevers' do
    # ...
  end
end

Using subject

This gem sets the RSpec subject to the response, after making the request. This allows you do use is_expected or expect(subject) to check the response.

describe 'GET /thing' do
  it { is_expected.to be_ok }

  # OR

  it "responds with 200 OK" do
    expect(subject).to be_ok
  end
end

Using params

There is a let for request params that starts as an empty Hash. You can either override it:

let(:params) do
  { my_param: 123 }
end

Or you can modify it in a before block:

before do
  params[:my_param] = 123
end

Using before blocks, you can do nested contexts:

context 'with a user' do
  before { params[:user] = { id: 123 } }

  context 'who is cool' do
    before { params[:user][:is_cool] = true }
    # ...
  end

  context 'who is not cool' do
    before { params[:user][:is_cool] = false }
    # ...
  end
end

Using headers

There is a let for request headers that starts as an empty Hash. This works exactly the same as params described above.

context 'requesting JSON' do
  before { headers['Accepts'] = 'application/json' }
  # ...
end

Path Interpolation

The path in the request description behaves kind of like a Rails route. Named segments that start with a : will be replaced by the value of the let (or method) with the same name.

describe 'POST /things/:thing_id' do
  let(:thing_id) { 4444 }
  # ...
end

It's common to want to use an attribute of an object that you already have a let for. To do that, use the # character.

describe 'POST /things/:thing#id' do
  let(:thing) { FactoryBot.create(:thing) }
  # ...
end

Attributes can be chained together.

describe 'POST /things/:user#things#first#id' do
  let(:user) { FactoryBot.create(:user, :with_things) }
  # ...
end

Hashs can also be interpolated by their keys, which can be either strings or symbols.

describe 'POST /things/:user#things#first#id' do
  let(:user) do
    {
      things: [
        { id: 1 },
        { id: 2 },
      ]
    }
  end

  #...
end

License

This gem is available as open source under the terms of the MIT License.