Rule of thumbs against flaky specs

Here are a few common patterns that will probably lead to flaky specs. If you notice them in your specs, please make sure that you have not introduced a flaky spec.

Using RSpec matchers

One rule of thumb I try to follow in capybara tests is using capybara matchers and not plain rspec matchers.

One example:

visit(some_page)
text_field = find('.textfield')
expect(text_field['value']).to match /pattern/

This can work, but is too brittle and flaky. match will not retry or synchronize the value of text_field.

The equivalent code with a capybara matcher:

visit(some_page)
expect(page).to have_field('.textfield', with: /pattern/)

have_field will retry for and synchronize the text_field.

Using .limit(N)

If your asserting a list of records that is somehow limited via a .limit clause, make sure your order is deterministic, otherwise you'll receive different records in different runs. For example:

# User(id bigint, ends_on: Date)
User.order(:ends_on).limit(5) # Bad, because the order is not deterministic if a lot of users end on the same date

User.order(:ends_on, :id).limit(5) # Better, because it takes the id into account in such a case

Using an external ui component library

If you're using an external UI component library, you've probably introduced a lot of flakyness to your spec. UI Components often introduce autoplay features, animations, take longer to initialize, are lazy loaded etc. Make sure you have a safe way to assert against the most glaring issues in your component. Often times, there's a good aria-* selector you can assert against. E.g.

expect(page).to have_selector('.modal[aria-hidden="false"]') # wait for the modal
expect(page).to have_css("[data-datepicker-loaded='true']") # make sure the datepicker is loaded before interacting with it
Niklas Hä.