0

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

1 Answers1

0

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.

Abdulaziz
  • 2,201
  • 1
  • 21
  • 35
  • Thanks Abdulaziz! That makes perfect sense, I thought `render_views` were just toggled in the RSpec config. And I ended up going the Capybara route after playing with this for an hour or so, was exactly what I needed. – jcksnparsons Mar 17 '21 at 14:15