So I'm working on my first Rails project and am working on getting some automated testing set up. Right now, I have a template for a form which has a select
tag, which is getting its information dynamically based on a mocked API response. As of now, my template successfully renders, but I would like to be able to access that select
tag and see if its child options
match with the my mock API response. Haven't been able to find a whole lot in the docs about this particular use case, hoping someone here may be able to point me in the right direction. Here's essentially what I have so far:
RSpec.describe MyController, type: :controller, do
describe "GET my_thing/index" do
it "renders the index template" do
stub_request(:get, "https://outsideapi.net/endpoint").
to_return(status: 200,
body: {
"values" => [
{ "name": "foo", "id": "10000" },
{ "name": "bar", "id": "10001" },
{ "name": "baz", "id": "10002" },
],
}.to_json,
headers: {})
get :index, params: { request_identifier: generate_request_identifier() }
expect(response).to have_http_status(:success)
expect(response).to render_template(:index)
# Some code that will hopefully get elements off the template
end
end
According to Rspec docs for controller tests:
by default, views are not rendered. See views are stubbed by default and render_views for details.
So first things first, you'll want to instruct Rspec to render your controller views by adding a render_views
directive in your test suite as mentioned here:
require "rails_helper"
RSpec.describe WidgetsController, :type => :controller do
render_views # <-- This
describe "GET index" do
it "has a widgets related heading" do
get :index
expect(response.body).to match /<h1>.*widgets/im
end
end
end
Finally, you'll be able to match the JSON array with the rendered HTML using somthing like this(code is untested, but it should hopefully drive the point):
RSpec.describe MyController, type: :controller, do
describe "GET my_thing/index" do
it "renders the index template" do
# Can also be added below the `describe` call if view rendering is needed elsewhere.
render_views
select_values = [
{ "name": "foo", "id": "10000" },
{ "name": "bar", "id": "10001" },
{ "name": "baz", "id": "10002" },
]
stub_request(:get, "https://outsideapi.net/endpoint").
to_return(status: 200,
body: {
"values" => select_values,
}.to_json,
headers: {})
get :index, params: { request_identifier: generate_request_identifier() }
expect(response).to have_http_status(:success)
expect(response).to render_template(:index)
# Extract the select value names as an array of strings.
select_value_names = select_values.map {|v| v['name']}
expect(response.body).to include(select_value_names)
end
end
On a related note, if you're running tests against page contents, you might want to consider using acceptance testing frameworks such as Capybara, which integrates well with Rspec.