Building Selectors
note
Make sure you've gone through Selectors before going ahead.
Writing selectors that just work is easy but writing selectors that are resilient to page changes require some practice. Carefully using test specific element identifiers in application helps in building strong selectors.
#
Available selector types and guidance on appropriate usage#
Element's textThis is most useful type of selector. It selects elements using their visible text. Using it makes your selectors transparent to changes as elements are selected by their visible identity and not something that is hidden in html source.
#
Element's attributesdata-testid
: Selectors that usedata-testid
are considered safe and resilient because this attribute is mainly used for testing and most changes to page shouldn't have an effect on these. It's a good practice to add it to page elements that need to be selected but don't have other strong selectors. Several popular applications on the web usedata-testid
to aid testing and there is absolutely no harm in keeping it in production.- Some elements that serve a specific purposes (such as text input or action buttons) usually have identifiers that don't change often such as
name
,title
,aria-label
androle
. Using these as selectors is considered safe. - Attribute
id
must be used carefully as some elements may have dynamic ids that must not be used. You can also make it a rule within your team to not useid
to prevent accidental use of dynamic ids. - Use of attributes such as
class
is discouraged and are prone to failure.
#
Element's tag- May be used sparingly for specific purposes. For example if you want to select the
header
element of a page, selecting it bytag
is fine as there will be only oneheader
element at the top browsing context of a page.
- May be used sparingly for specific purposes. For example if you want to select the
#
Css selectorCss selectors that depend on a specific DOM structure aren't considered safe and should only be used as last resort as they may break with changes to the page. See following example that searches a term in google and verifies the first result is intended:
openUrl('https://google.com')type(findElement('Search', by.ariaLabel), 'wikipedia', keys.enter)firstResult = findElement('div[role="main"] div[data-async-context="query:wikipedia"] > :first-child', by.cssSelector)assertTrue(containsString(getElementText(firstResult), 'Wikipedia, the free encyclopedia'))
In 3rd line we've used a css selector that does the job of finding the first result but it is not safe and prone to failure in future. It assumes that inside the main
div
, there is an innerdiv
with attribute 'data-async-context' that is the container of all results and we want it's first child to get the first result. If in future, the result container is something else than the one we're assuming is or the result container start to contain some other node as first child rather than the intended result, this selector will fail.If you find yourself in similar situations, assigning
data-testid
to elements almost always solves the problem. For the google search example, imagine if every result contains adata-testid = searchResult
we could've changed our selector to a much cleaner and stronger one like so:firstResult = findElement('searchResult', by.testId)
Which selects the first result and it is extremely safer and faster than before.
#
Things to consider while building selectors#
Never use a 'tool' to auto generate selectors for your application.Instead, understand your application and your tests requirements. Little additions to html can make it very easy to write strong selectors. If you follow the guidance on appropriate usage, you'd soon realise the selectors you really ever need are the ones that need plain
string
such aselement text
,data-testid
or some other safe element attributes.In some cases when you're testing a legacy app and can't make additions to html (to add test specific identifiers like
data-testid
), css selectors may be the most viable selector option. We'd still advice against using a 'tool' as they may generate brittle css selectors that could depend on random class names or multi level child selectors. Instead, learn to write css selectors and write your own. We suggest you learn from this mdn article on css selectors. While you learn, use chrome/firefox developer tool'sconsole
tab to test your selectors (such as usingdocument.querySelectorAll('YOUR_CSS_SELECTOR')
)#
If you really have to use DOM dependent css selectors, try to keep them short and less brittle.In some cases, it may be unavoidable to write css selectors. If you write them, try making them less brittle. Try not to use class names and don't make too much use of multi level child selectors or sibling selectors.
Always test your css selectors before using them in code. Use chrome/firefox developer tool's
console
tab and evaluate what your selector yields. For example,document.querySelectorAll('YOUR_CSS_SELECTOR')
selects all nodes that matches specified selector. Confirm that your selectors are optimized and less prone to breakage before using them in tests.#
Always narrow down the scope of your searches wherever possibleWhen you build a selector and invoke one of the function to get the required element, webdriver searches that element in the DOM before returning it. If you don't specify a parent element to start from, webdriver starts from the root of DOM consuming more time to search. To speed up your tests, always use a parent element if you've one. You should always do this when you need to search multiple elements within the same parent element. To do so use functions such as
findElementFromElement(elemId, using, by)
, get the parent element once and use it in further searches.
Later chapters will discuss all available selectors in greater detail.