Having confidence in your application helps with the aid of great interation tests. For Rails, that means utilizing the Capybara gem for testing features from end-to-end.
While Capybara is a great gem for accomplishing this goal, it can be at time difficult, frustrating, and nuanced in its implementation. I’ve been keeping track of all the tricks I use reguarly and compiled a list of the best ones. Hopefully, this helps give some clarity to a few best practices I use on a daily basis.
Find element attribute
You can use the hash symbol syntax to return the full value of any attributes from a captured element.
The additional syntax of
find(".PathContentOptions .NewButton")[:disabled] is also available but
not recommended due to its inability to wait for the element to load fully with the specific
Find the parent of an element
Sometimes it might be nearly impossible to grab a specific container element. Whether that is due to multiple existing on the page or insufficient selector specificity, it can be difficult to grab ambigous elements.
However, if you can find an element that is a child of the element you want to write expectations against you can use xpath to find its parent.
Set element value without an ID or name
Using the keyword
set you can mimic the fill_in shortcut for elements that are difficult to find by id or name.
You can also select options from a drop down in a similiar fashion with the
Mimic user typing directly with send_keys
While the previous example gives you a way to directly set what value is within an element, sometimes you want to actually mimic the user’s interaction. This is especially important with things like autocomplete or fuzzy searches.
Native returns the element directly from the driver allowing for more control of
elements that is closer to the real implementation of the browser.
Send_keys mimics a user typing each character one at a time into whatever element
it is pointed at.
Assert that the button is disabled
This allows you to match only disabled buttons on the page. Without this line Capybara defaults to marking elements that are disabled as not being part of the content.
Furthermore, you can also check for visible buttons or elements with the visible attribute.
Switch your test context to a popup
By switching to the latest browser window as the current page window you can seamlessly test a popup’s interactions and styles.
Ensure specific counts for element matches
Allows for matching and expecting a certain number of elements. Fails if there are more or less than the specified amount.
Expect selector with text
Matching a css class or ID can sometimes not be specific enough. Especially, in cases where a class appears more than once further matching the selector based on the text within it can help increase expectation specificity.
Additionally, text can accept regexp syntax so you can use the
/i case insensitive matcher
Waiting for ajax? Sleep no more!
Waiting for ajax can be a pain. How long should sleep be set for? A quarter-second? 1 second? Luckily, there’s a way to properly wait for ajax to complete without using sleep.
The original solution (which the above is based on) was proposed by Thoughtbot and can be found here on Coderwall.
Poltergeist web driver clicking elements
Error message: Capybara::Poltergeist::MouseEventFailed:
The element you are trying to interact with might be overlapping another element. If you don’t care about this, using node.trigger(‘click’) can ignore
Search within particular element context
You probably know that you can change the page context with the
within(".selector") do block. But did you know you can also
select a specific element based on where it appears?
Note Make sure you use an option or element waiting approach when using first() since first() doesn’t wait until all elements load.
Use current_scope to debug the current context
If you wanted to debug the html from within a within block you unfortunately can’t do something like:
Luckily, there is a keyword that allows you to access the current scope called current_scope
Directly debugging the current page
save_and_open_page isn’t enough to determine what is going
on within a test. It doesn’t load assets properly or interact well with user
However, there is a trick to actually debugging the state of the current page within a test.
You’ll end up with a printed url within your test suite which can then be copy-pasted into your browser where you can interact with the current page. Using binding.pry pauses execution right where you want the state of the page to be currently. I can’t take credit for this trick as the excellent post on QuickLeft directed me to it.
Avoiding flaky tests
Another article by the Thoughtbot team, about all the ways of avoiding race condition like tests. I’ve gleaned some of my information from this article which can be found here
Remove css animations for less flakes and a faster test suite
By default poltergeist, and potentially other js drivers, perform css animations. Depending upon your expectations this might slow your test down in that expectations need to wait until an element is fully loaded. Additionally, this could impact the consistency of your tests by introducing flaky race conditions.
Instead, if we disable animations altogether we should in theory have a faster test suite that is more reliable. I found the basic idea for this on StackOverflow.
Capybara::Poltergeist::Driver.new is registered with the following:
Using binding.pry as described above can help track down where errors are occuring in the execution flow and is generally the most useful approach.
If you are constantly receieving errors it is possible that there is a network issue preventing the test from passing. Yes, a local network setting could prevent the test suite from passing in development. Here’s an example of an ISP blocking and redirecting a request being made to the jquery cdn from a feature spec.
The give away here is that jQuery is being required by Bootstrap but Bootstrap can’t find it. At the end of the error in the console the url returned is suspicious http://dnserrorassist.att.net/s/js/bootstrap.min.js:6. Specifically the highlighted part indicates that the request is be re-routed by the network.
In order to fix these type of issues you’ll need to change your network settings on your router, adjust privacy settings with your ISP, or if you are on public WIFI move to a new location.
Do you have an awesome Capybara trick I missed? Let me know with a comment below.
Thanks for reading.