0

I am currently working on a project where the xpath of the elements changes continuously. At the UI display level there is not much visible change in UI, however the DOM elements changes continuously.
I am thinking of a better approach to handle failures in test case due to minor changes in DOM structure. I am currently using Selenium with TestNg framework for UI Automation Testing.
Any suggestions or directions on alternate approach would be helpful.

Krishnan Mahadevan
  • 14,121
  • 6
  • 34
  • 66
vaishakh
  • 176
  • 2
  • 2
  • 14
  • By the phrase `better approach` you you mean **Artificial Intelligence**? – undetected Selenium Apr 23 '18 at 08:19
  • For eg :- a save button would be more or less in the same position if there is a small change in DOM structure. Tracking the sequence of events and adding a metadata to the click events would help. So in case of failure I can check for alternate buttons and continue my execution. – vaishakh Apr 23 '18 at 09:07
  • `a save button would be more or less in the same position if there is a small change in DOM` apparently **True** but logically/functionally/factually **False** – undetected Selenium Apr 23 '18 at 09:09

3 Answers3

2

Are you following the orders of locators suggested by Selenium contributors: if not then please follow this order :

  1. ID
  2. name
  3. className
  4. linkText
  5. partialLinkText
  6. tagName
  7. cssSelector
  8. XPath.

Note : Most of the time a cssSelector can replace Xpath, However Xpath has its own advantages which cssSelector do not provide.

For more reference you can go through this SO Link : Css vs Xpath

cruisepandey
  • 28,520
  • 6
  • 20
  • 38
0

My suggestion would be: 1. Try using different locators for particular element[https://api.jquery.com/multiple-selector/] something like selector1, selector2, selectorN. If selector1 is not available in DOM, control will not throw an error instead, it will search selector2 and so on 2. Use Explicit waits

Magesh
  • 308
  • 1
  • 4
  • 18
  • We already using multiple selectors in some places , but the issue is most of the elements don't have id and sometimes in xpath there is a small change in DOM Structure which causes the test case to fail. – vaishakh Apr 23 '18 at 07:33
0

I use By.CSS selectors rather than by xpath, as these are less prone to changes in the DOM.

So for this example dom:

<div class="smc-login-container">
<form role="form" action="/login.html" method="POST" name="login" class="ng-pristine ng-valid">
    <!-- Username -->
    <label for="username">User ID:</label>
    <input type="text" class="smc-login-control aftLoginUsernameField" id="username" name="username">

    <!-- Password -->
    <label for="password">Password:</label>
    <input type="password" class="smc-login-control aftLoginPasswordField" id="password" name="password">

    <!-- Cross site scripting token -->
    <input type="hidden" id="_csrf" name="_csrf" value="efaa05c4-77a8-443d-9484-51e8c9795c28">

    <!-- Sign In Button -->
    <button id="signIn" class="btn btn-lg btn-block smc-login-button aftLoginSignInButton">
        Sign In
    </button>
</form>

These selectors work:

Example 1. Find Button, element type and class

WebElement element = webDriver.findElement(By.cssSelector("button.btn"));

Example 2. Find Button, direct child rather than descendent

Uses full DOM and thus form element:

WebElement element = webDriver.findElement(By.cssSelector("div > form > button"));

Doesn't use form element:

WebElement element = webDriver.findElement(By.cssSelector("div button"));

Example 3. Find Input following the username label, attribute with value then it’s sibling (+)

WebElement element = webDriver.findElement(By.cssSelector("label[for='username'] + input"));

Example 4. Find Password input and Button, OR’d (comma) returning more than one element

List<WebElement> elements;
elements = webDriver.findElements(By.cssSelector("input.smc-login-control.aftLoginPasswordField , .btn"));
elements.get(0).sendKeys("my password");
elements.get(1).click();

Example 5. Find username input, has class attribute, but not attribute name = ‘password’:

WebElement element = webDriver.findElements(By.cssSelector("input[class]:not([name='password'])"));

Another thing I do is use a more generic pattern, which finds multiple items. I've found the items are always found in the same sequence and thus I can access the one I want by just using the array index (like in example 4).

Finally, if the DOM has dynamic values I've found that I can use a specific stable pattern to find a parent element which has the same dynamic value, thus extract it and then reuse it to find the other element I actually want.

elworthy
  • 454
  • 4
  • 12