Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save calaway/1becbcb672082b6126f115df0261d023 to your computer and use it in GitHub Desktop.
Save calaway/1becbcb672082b6126f115df0261d023 to your computer and use it in GitHub Desktop.
Best Practices for Building CSS Selectors in UI Test Automation

Precise

A good selector matches exactly the intended element(s) and no more.

I recommend always building out your selectors in a pry session. First build your selector using Capybara's all method with .count to make sure your selector does not return more elements than you are intending to target.

page.all('[build selector here]').count

Once you have your selector built this way, swap out the page.all('...').count for the capybara method you need, e.g. find('...') or assert_selector('...').

Specificity Goldilocks Zone

A good selector is the least specific necessary to match the intended element(s), while still being resilient to future changes.

Too specific:

find('nav > ul > li:nth-child(2) > a')

This will break if a future <div> is wrapped around the <ul> or if another list item is added making this the third child.

Not specific enough:

find('a', text: 'Tasks')

It's easily conceivable that in the future the page includes another link that includes the same text, making us act upon the wrong link.

Good:

find('a.activityMenu__link', text: 'Tasks')

It's safe to assume there will never be a second activity menu link with this same text.

Good:

find('nav.mainNavigation a.tasks-link')

It's safe to assume there will never be a second tasks link under the main navigation.

Readability

I recommend always including the HTML tag in all selectors, rather than just the class or id, to increase readability.

Bad:

find('#main #content-wrapper .item')

If I'm reading this code and referencing the HTML to find out what element(s) it is targeting, this selector doesn't give me any hints as to what tags I should be looking for.

Better:

find('nav#main div#content-wrapper ul.item')

This allows me to quickly scan the HTML for a <nav> element, then a <div>, and then a <ul>.

Resilient

Don't use selectors that are meant for styling

Bad:

find('a.psm-default-button', text: 'OK')

This is not resilient to minor UX changes. It will break if they change the color of the button, for example by changing the class to psm-primary-button.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment