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
Hash
s 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.