Martin McGee's Blog

The Ultimate Element Selector Masterclass for Automating the Web

May 13, 2020

Code

There is nothing more painful than inheriting someone’s automation framework, with flaky tests and selectors that look like this:

/html/body/div[1]/div/div/div[2]/main/div/div/form/div/div[1]/label/div/div[2]/div/input

😱

This is a real example I have found, believe it or not!

If you write/use selectors like the above, please stop!

Brittle element selectors are the number one cause of flaky automation tests - if a developer adds a div to the page, your test is 💩!

Writing GOOD resilient selectors is EASY and ANYONE CAN learn it and it WILL save you time and a lot of headaches with a little thinking.

I will show how to select the most difficult elements in the best way possible, making your automation tests more resilient to change.

Selectors in modern javascript frameworks

Generally, it’s best practice to select every element by id, which is great in a perfect world. However, in our day to day work, this is usually not the case.

You may have inherited a legacy app with 0 id’s or work with developers who don’t have time to add them in. Modern test frameworks like React and Angular have auto generated id’s which make selecting by id impossible and the dom structure is messy.

There are also some guidelines in tool docs like cypress, in which they advise to use tool specific attributes eg. data-cy - again, this is assuming a perfect world where developers will add those attributes in during development.

XPath v CSS

Before we get into element selection let’s clear the XPath v CSS debate:

Which one is the best?

Simply It does not matter, the best one to use is the one you and your team understand best. They perform the same job, and both are readable. Use both of them or only one of them, the choice is yours.

But..but…CSS is faster than xPath!

Yes, it’s a few microseconds faster, but you are building test automation, not a banking algorithm.

Now that’s settled…

The General Recommended hierarchy for finding a unique selector

The standard process is inspecting an element, then looking for something unique about it.

General hierarchy of selecting a unique attribute in order:

  1. Id
  2. Name
  3. Text
  4. Everything else

For example, we have a login page with a button that looks like:

<button id="login" class="btn btn-large" name="login"
  role="button">Login</button>

Ok, that’s easy, just use

Css: #login
Xpath: //button[@id=’login’]

Ok let’s look at a real-life example - the Twitter login page: Twitter Login Page

Here is the username input box element:

<input autocapitalize="none" autocomplete="on" autocorrect="off" name="session[username_or_email]" spellcheck="false" type="text" dir="auto" data-focusable="true" class="r-30o5oe r-1niwhzg r-17gur6a r-1yadl64 r-deolkf r-homxoj r-poiln3 r-7cikom r-1ny4l3l r-1inuy60 r-utggzx r-vmopo1 r-1w50u8q r-1swcuj1 r-1dz5y72 r-1ttztb7 r-13qz1uu" value="">

This is a more realistic example that we get in modern-day javascript apps.

But where is the element id? What should I do here??

Ok, let’s try the name.

Xpath: //input[@name=’session[username_or_email]’]

Nope - 2 elements found not unique!

Class name?

No, it looks like it’s using the auto-generated class name that will change rapidly!

Ok, im bored now, let use a popular chrome addon to find the xpath for us:

/html/body/div[1]/div/div/div[2]/main/div/div/form/div/div[1]/label/div/div[2]/div/input

Unique? Yes

Terrible xPath that will break? Yes. 😱

So lets come up with a new strategy

So we found that the above strategy works in a perfect world, but not in a chaotic world like that of software development, so what would be the approach in the new strategy?

The context of the page that you are getting the element from comes into play more now to choose a good selector.

So going back to the twitter input example above let’s look at the login page:

We see 2 inputs and a button, we want to select the first input.

Ok let’s think about the typical login pages you have seen, nearly all of them have that format above, the only elements on the page are 2 inputs and a button - the username is the first input, the password is the second input and then a button.

Right, so going by the above, the username field will usually always be the first input on the page and is unlikely to change so to select it we could use:

Xpath: //input[1]

No, there are usually hidden elements on the page like:

<input name="wfa" type="hidden" value="1">

So actually it is not the first input on the page, so what can we do now?

Usually, invisible elements have a hidden style added to them, so we can use that combined with the above?

So, now we want to select the first input element that is not hidden:

//input[not(@type='hidden')][1]

Cool, looks better than

/html/body/div[1]/div/div/div[2]/main/div/div/form/div/div[1]/label/div/div[2]/div/input

And more relliant to change, but can we do better??

So we have 2 inputs, both visible, let’s compare them again and see if we can spot any differences:

Username input:

<input autocapitalize="none" autocomplete="on" autocorrect="off" name="session[username_or_email]" spellcheck="false" type="text" dir="auto" data-focusable="true" class="r-30o5oe r-1niwhzg r-17gur6a r-1yadl64 r-deolkf r-homxoj r-poiln3 r-7cikom r-1ny4l3l r-1inuy60 r-utggzx r-vmopo1 r-1w50u8q r-1swcuj1 r-1dz5y72 r-1ttztb7 r-13qz1uu" value="">

Password input:

<input autocapitalize="none" autocomplete="on" autocorrect="off" name="session[password]" spellcheck="false" type="password" dir="auto" data-focusable="true" class="r-30o5oe r-1niwhzg r-17gur6a r-1yadl64 r-deolkf r-homxoj r-poiln3 r-7cikom r-1ny4l3l r-1inuy60 r-utggzx r-vmopo1 r-1w50u8q r-1swcuj1 r-1dz5y72 r-1ttztb7 r-13qz1uu" value="">

Differences:

So it looks like the types are different, so for the username we could use input where the attribute type=text and for password, we could use input where the attribute type=password

So username looks like:

//input[type="text"]

And password looks like:

//input[type="password"]

(Remember the other inputs are type=hidden)

So is this a GOOD selector? Let’s rate it:

1. Is it easy to read and understand? ( not /div/html/h1/ etc)? YES

2. Is it resilient to change? Given the context that there is usually only 2 input boxes on the page then yes, adding images, other text to the page will not break it, and it is unlikely there will be more than one input with these types added to the page.

So with the above points, the new and improved strategy would be: With a bit of research on the page, selecting by any attribute is fine as long as the context of the page is taken in to consideration and it meets the scoring criteria above.

So as you can see there is usually always a way of finding a good, readable, resilient page object even when the page structure is chaos! 👍

Bonus Round:

Just tell your developers to add test-id attributes to your elements - simple!

Sure, but it will be marked as a low priority in the Jira backlog and will probably never see the light of day!