How to use and test an AJAX request in Rails

Photo by Crew on Unsplash

How to use and test an AJAX request in Rails

What's AJAX?

AJAX (Asynchronous Javascript and XML) is a kind of technique that can send HTTP requests and exchange data with a server by Javascript. It's an important and practical technique to enhance the user experience, especially when we want to achieve the effect that updating the partial content and wouldn't like to reload the whole page.

How to send an AJAX request in Rails

Rails has built-in Unobtrusive JavaScript that has required in the application.js file.

//app/assets/javascripts/application.js

//= require jquery
//= require jquery_ujs

Thanks to Unobtrusive JavaScript! We can just put something like data-* on the HTML DOM and some simple dynamic things can be implemented. (Read more about the list of data attributes)

The most common one is data-confirm. After adding data-confirm like the example below, when a user clicks the Delete, a pop-up dialog with 'Are you sure to delete this event' texts will show up.

<%= link_to(
  'Delete', 
  event_path(event), 
  method: :delete, 
  data: { confirm: 'Are you sure to delete this event?' } 
 )
%>

We can implement an AJAX request by the data attributes that Unobtrusive JavaScript has provided, too. Just like the example above, we just need to add data-remote on the target HTML DOM. After that, Rails will help us use AJAX to send the HTTP request.

For instance,

<%= link_to 'Show the article', article_path(article), remote: true %>

Rails helper will generate HTML tags like:

<a href="/artciles/1" data-remote="true">an article</a>

How to test an AJAX request with RSpec

For a general GET request, the test subject might look like this:

RSpec.describe ArticlesController, type: :controller do
  describe 'GET #show' do
    subject { get :show, params: params }

    let(:params) { { id: article.id } }

    it { is_expected.to have_http_status(:ok) }
    it { is_expected.to render_template(:show) }
  end
end

To make an AJAX request to the show action using GET,

we have to add xhr: true to the subject (Rails version 5.0+):

RSpec.describe ArticlesController, type: :controller do
  describe 'GET #show' do
    subject { get :show, params: params, xhr: true }

    #...
  end
end

According to Rails ActionController::TestCase,

# rails/actionpack/lib/action_controller/test_case.rb:569

if xhr
  @request.set_header "HTTP_X_REQUESTED_WITH", "XMLHttpRequest"
  @request.fetch_header("HTTP_ACCEPT") do |k|
  @request.set_header k, [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
  end
end

I'm curious if it is an AJAX request now, so I try to check it out by calling request.headers["X-Requested-With"] and request.xhr?.

# rails console

  def show
=>  binding.pry
    #...
  end

 $ request.headers["X-Requested-With"]
 #=> "XMLHttpRequest"

 $ request.xhr?
 #=> 0
 # means the value of headers["X-Requested-With"] is totally match "XMLHttpRequest" string

The answer is yes! It's an AJAX request now.

The process of AJAX interacting with the server is by an XMLHttpRequest object. After setting xhr: true means the “X-Requested-With” header has contained “XMLHttpRequest” and tells the client to send the request by AJAX.


Reference