How to use and test an AJAX request in Rails

How to use and test an AJAX request in Rails
Photo by Nick Morrison / Unsplash

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:

Getting Started - Developer guides | MDN
This article guides you through the AJAX basics and gives you some simple hands-on examples to get you started.
Working with JavaScript in Rails — Ruby on Rails Guides
Working with JavaScript in RailsThis guide covers the built-in Ajax/JavaScript functionality of Rails (and more); it will enable you to create rich and dynamic Ajax applications with ease!After reading this guide, you will know: The basics of Ajax. Unobtrusive JavaScript. How Rails’ built-in helpers…
Unobtrusive scripting support for jQuery (list of data attributes) · rails/jquery-ujs Wiki
Ruby on Rails unobtrusive scripting adapter for jQuery - Unobtrusive scripting support for jQuery (list of data attributes) · rails/jquery-ujs Wiki
Ajax 應用程式
介紹 Ruby on Rails 這套開放原始碼的網站開發框架
Rails Testing XHR with Post data | Newbedev
For me, using RSpec 3.6 and Rails 5.1 the previous answer fails: xhr :post, :index # => NoMethodError: undefined method `xhr’ for #<RSpec::ExampleGroups::XContr
rails/test_case.rb at main · rails/rails
Ruby on Rails. Contribute to rails/rails development by creating an account on GitHub.