RSpec (v6) Rails Request Specs
Request specs are a thin wrapper around Rails integration tests. Similar to controller tests, request tests allow you to test controller actions. Unlike controller tests, request tests involve routing, middleware, and rack requests/responses.
An RSpec test can be marked as a request spec by adding meta-data type: :request
. This is not required if RSpec is configured to infer_spec_type_from_file_location
. In this case, you can add a request spec to the spec/requests
folder and skip the meta-data.
# spec/requests/posts_spec.rb
RSpec.describe "Posts requests" do
end
# spec/any/other/location/posts_spec.rb
RSpec.describe "Posts requests", type: :request do
end
Assertions
render_template
redirect_to
Capybara
Capybara is not intended to be used with Rails request specs and is not supported. However, you could use Capybara with Feature specs.
Example
it "creates a Post and redirects to the Post's page" do
post api_posts_path, params: { post: { title: "RSpec (v6) Rails Request Specs",
body: "Request specs are a thin wrapper around Rails integration tests." } }
expect(response).to redirect_to(assigns(:post))
follow_redirect!
expect(response).to render_template(:show)
expect(response.body).to include("RSpec (v6) Rails Request Specs")
end
Requesting a specific content type
The default content type is HTML. Should you need to test against specific content types (ex: turbo stream, JSON etc) you can specify the header.
# Calling an endpoint responding to Turbo Stream
it "loads a post" do
get post_path(current_post, format: :turbo_stream)
expect(response).to have_http_status(:ok)
expect(response.content_type).to eq("text/vnd.turbo-stream.html; charset=utf-8")
end
# Calling an endpoint responding to JSON
it "loads a post" do
get post_path(current_post, format: :json)
expect(response).to have_http_status(:ok)
expect(response.content_type).to eq("application/json; charset=utf-8")
end
Submitting specific content type
Let's assume you intend to submit data to a JSON endpoint. You can provide the data as a JSON string, or better, use a hash and convert it to JSON.
it 'creates a post' do
post api_posts_path, params: { post: { title: "RSpec (v6) Rails Request Specs",
body: "Request specs are a thin wrapper around Rails integration tests." } },
headers: { "ACCEPT" => "application/json" }
expect(response).to have_http_status(:created)
expect(response.content_type).to eq("application/json; charset=utf-8")
end
Testing subdomains
Most modern applications utilise subdomains. You need to use the host!
method to use a specific subdomain.
before { host! "api.example.com" }
it "creates a Widget" do
post api_posts_path, params: { post: { title: "RSpec (v6) Rails Request Specs",
body: "Request specs are a thin wrapper around Rails integration tests." } },
headers: { "ACCEPT" => "application/json" }
expect(response).to have_http_status(:created)
expect(response.content_type).to eq("application/json; charset=utf-8")
end
About the Author
Ziyan Junaideen -
Ziyan is an expert Ruby on Rails web developer with 8 years of experience specializing in SaaS applications. He spends his free time he writes blogs, drawing on his iPad, shoots photos.