Today I Learned: Capybara better practices

chandrahas raj
Kiavi Tech
Published in
2 min readFeb 4, 2019

Never re-invent the wheel

Capybara feature specs can be frustrating because it’s difficult to get them to pass 100% of the time. This post will teach you how to avoid a certain race condition, using only built-in Capybara methods.

Often times, it’s easy to wonder why your specs run so slow or why your spec is blinky. Most of the times it is because we’ve used something that just works( or maybe not). You could, of course, take a few minutes to read about it before using it, but since you’re probably as lazy as me, here’s a blog post explaining it.

For example, let’s imagine you are writing a spec that clicks on something, waits for a background task to be completed which updates a certain field, and then finally validates that field. (Oofff…lots of steps.) There are many ways to write this spec:

# spec/features/user_page_spec.rbdescribe UserPage do
it "validates text on the form"
click "validate"
expect(find('.sample_form_text').text).to eq('validated text')
end
end

The above spec works just fine, but it can be blinky. How?

Let's set some assumptions early before I go ahead:

Capybara.default_max_wait_time = 5. I have the set the default_max_wait_time of capybara to 5 seconds.

Now let’s go through each step. First, you click on validate on the page and the background task gets triggered. Now it’s going to try to find the text on the class: sample_form_text and validate it with validated text. In most cases, this works fine. But this call find('.sample_form_text').textis almost instantaneous. So if the background task takes some time to fill the page with the text, the spec is going to fail. How can we fix this?

One method I like is from this post, https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara

# spec/support/wait_for_ajax.rbmodule WaitForAjax  def wait_for_ajax    Timeout.timeout(Capybara.default_max_wait_time) do    loop until finished_all_ajax_requests?  endend  def finished_all_ajax_requests?    page.evaluate_script(‘jQuery.active’).zero?  endendRSpec.configure do |config|  config.include WaitForAjax, type: :featureend

So you can use the background task as shown below:

click "validate"
wait_for_ajax
expect(find('.sample_form_text').text).to eq('validated text')

Another option is to use something capybara already provides. Shown below:

Before:click "validate"
expect(find('.sample_form_text').text).to eq('validated text')
After:click "validate"
expect(find('.sample_form_text', text: expected_text)).to have_content(expected_text)

By default, the find with text option waits for the text to be available and that fixes the blinkiness.

Hope this helped! We’ll be back soon with another edition of Today I Learned

References:

--

--