Testing AJAX-Reloaded Elements with Capybara

Michał Szajbe

Testing AJAX-Reloaded Elements with Capybara

Capybara is a great tool for testing application flow and user interfaces. Thanks to Culerity and Selenium web drivers you can test javascript and AJAX features of your apps.

However testing AJAX is not that straightforward because of asynchonous nature of these requests. In order to test effects of AJAX calls a web driver must wait until they finish which is difficult if not impossible to detect. However this can be worked around using some tricks.

Scenario 1

Let's say you want to test a link on the page that triggers an AJAX request which eventually inserts some element in DOM. This could be an Edit link that retrieves object's data from database (via AJAX call) and then shows an HTML form on the page. Test scenario might look like this:

@selenium
Scenario: Clicking "Edit" should show edit form
  Given I am on product's page
  And I click "Edit" link
  Then I should see form

The form will not appear immediately after the link is clicked because it takes some time for your app to process the AJAX request and return the response. Capybara is intelligent enough to acknowledge this and instead of expecting a form to appear immediately it periodically looks for it in page's DOM. You can define for how many seconds it should keep looking by setting following option:

Capybara.default_wait_time = 5

If the form doesn't appear in this specified time frame, the test fails. Note that Capybara doesn't always wait full 5 seconds, it simply moves on to the next step as soon as the form appears.

 Scenario 2

Now you want to test a link that removes an element from DOM. This could be a Save link that saves object to database (via AJAX call) and then removes the form. Test scenario:

@selenium
Scenario: Clicking "Save" should remove edit form
  Given I am on product's page
  And I click "Save"
  Then I should not see form

This test fails even if your app works as expected. I guess you see the problem. Capybara finds the form on first lookup which is performed immediately after the click, but at that point the AJAX call has not completed yet, so the form is still there.

Popular solution is to explicitely tell Capybara to wait until it starts looking for changes in DOM. In other words, give the AJAX calls chance to complete. Here's an adequate cucumber step:

Given /^I wait (\d+) seconds?$/ do |sec|
  sleep(sec.to_i)
end

Rebuilt scenario would look like this:

@selenium
Scenario: Clicking "Save" should remove edit form
  Given I am on product's page
  And I click "Save"
  And I wait 5 seconds
  Then I should not see form

The downside is that it will always wait 5 full seconds now.

Other aspects

Neither explicit waiting with sleep, nor Capybara's default_wait_time option guarantees 100% success. Your tests may still occasionally fail if the AJAX requests they perform takes longer than you assume. The time it takes for the app to process such request may be quite random as it depends on many aspects like machine load or external services it hits (database, search engines, etc.). So remember to set the option to a value high enough for your app.

Michał Szajbe avatar
Michał Szajbe