5 Tips for More Effective Capybara Tests
Capybara is an acceptance testing framework for web applications. Here are five useful tips for writing better tests using Capybara.
In this article, we'll cover five tips for writing effective Capybara tests, and how to use them with RSpec and Minitest.
What is Capybara?
Capybara is an acceptance test framework for web applications. It's a common choice for end-to-end, acceptance, or integration testing in Rails applications.
It allows developers to simulate a user on a web page and make assertions based on the content and environment of the page. It also provides an API to interact with the web page.
Integration and Acceptance Testing
The terminology for integration and acceptance testing is quite broad within the Rails community. Most of the time Capybara is used in integration tests, e.g.feature specs in RSpec, to test all workflows in your web application from user perspective. The goal is to test whether all components of your application work together.
RSpec and Minitest
RSpec and Minitest are the two most commonly used test frameworks for Rails applications. Capybara works with both of them, but the syntax can differ slightly. We won't go into details about RSpec and Minitest in this tutorial, however, if you'd like to know more about them you can check out our Getting Started with RSpec and Getting Started with Minitest tutorials.
Five Tips for Writing Capybara Tests
1. More Robust Element Search with Scopng
If you have a large page, scoping your Capybara page assertions can be considered a good practice. It can solve unexpected problems with duplicate content on the page.
For example, if you're working on a component which has a sign up button, when there's already a sign up button further up the page, you might end up never testing the second one unless you scope your page lookup query.
<div class="header-cta"> <a href="/signup" class="btn btn-signup">Sign up</a> </div> ... <div class="article-cta"> <a href="/signup" class="btn btn-signup">Sign up</a> </div>
A way around this is to use
within to scope the lookup.
within('.article-cta') do click 'Sign up' end
within method also supports using XPath rather than element names, IDs,
within(:xpath, '//div[@class="article-cta"]') do click 'Sign up' end
If you're performing only one action inside the block you pass into
the same can be accomplished using
find('div#article-cta').click 'Sign up'
It's worth mentioning that Capybara also has specific methods for scoping
within_table and fieldsets
within_fieldset. It's also possible
to use all the other matches supported by Capybara with
within, such as
XPath, but it defaults to the CSS matcher.
First, install the gem by adding it to your gemfile.
Then, load it in your test setup file (
Ruby. Here, we store the value in
button_text = page.evaluate_script("$('a.btn-signup').text();")
In RSpec, we add a
js: true as a second argument for
In Minitest, we have to literally tell Capybara to switch drivers, and then switch back.
These could also be performed in Minitest's
teardown methods, if
Capybara.default_driver = :poltergeist
Thankfully, the Capybara developers have provided us with tools to get around
find method waits by default for an element to appear for
a certain amount of time. It also accepts count expectations which tell Capybara to
wait for the expectations to match when using
all. There are four different
count expectations. The first one matches on exact count.
all 'a.btn-signup', count: 1
Next two methods allow us to specify a minimum or maximum number of elements that need to be matched.
all 'a.btn-signup', maximum: 1 all 'a.btn-signup', minimum: 1
The final count expectation lets us provide a range and the number of matches must fall within this range.
all 'a.btn-signup', between: 1..1
Each of them has different uses,
all otherwise works the same as it always
does. The default maximum time Capybara waits for the expectation to be
fulfilled can be configured in your test setup file. The time is measured in seconds and
the default value is two.
Capybara.default_max_wait_time = 5 # Seconds
all 'a.btn-signup', count: 1, wait: 5
4. Saving Pages for Debugging
Capybara has two different ways to help with debugging. Although something like pry is useful if you want to see the state of your variables, sometimes we want to know what is going on with the HTML on our web page.
For those situations, we can use
save_and_open_screenshot. They are called in the same way with both RSpec and
scenario 'debugging a failing test' do visit root_path save_and_open_page # Rest of your test end
This will save the HTML of the web page in a temporary file and open it in your
browser. Styles will be missing, but it's enough to see what state your web page
is in and what's going on to spot the problems.
save_and_open_screenshot works in exactly the same way, except that it opens an
image file of your web page.
5. Use Different Element Matching Strategies
By default, Capybara matches on partial matches. Both of these click method calls match on the sign up button we created earlier.
click 'Sign' click 'Sign up'
It's possible to change this behavior by passing in
exact: true. This
click method call does not match on the signup button.
click 'Sign', exact: true
It's also possible to change this to be the default behavior in your test setup.
Capybara.exact = true
It's also possible to configure how Capybara handles matching. There are four
built in methods. The first one is called
:first and it makes
return the first match and ignore the rest.
Capybara.match = :first
The second one is
:one, which makes Capybara raise an error when there is more
than one match. Using this setting can prevent many tests from breaking without
your knowledge, because of more than one of the same elements on a page.
Capybara.match = :one
The third one is
:prefer_exact, which makes Capybara return an exact match if
one exists, otherwise it will return a non-exact element.
Capybara.match = :prefer_exact
The final and default is
:smart. It always raises an error if there is more
than one match, similar to
:one. However, for matching, the behavior changes
depending on if
false. When exact is
false, it tries to
find an exact match, but if none is found it tries to find partial matches.
Capybara.match = :smart
In this article, we have covered five useful Capybara testing methods in detail.
If you found this article useful, you might want to check out other articles on testing in the Semaphore Community. We'd also like to hear your comments and questions, so feel free to leave them in the section below.
P.S. Would you like to learn how to build sustainable Rails apps and ship more often? We've recently published an ebook covering just that — "Rails Testing Handbook". Learn more and download a free copy.