When planning and implementing web applications in any language for the web, one thing can be sure, your output will be html, css and javascript, regardless of the framework you used to build your application. I use the term application loosely here to describe anything you load into your browser or browser based technology, such as electron

Your code, whatever flavour will (hopefully) execute in the browser and with that in mind, we have an advantage when it comes to testing this application: standards.

Most automated testing suites have a series of tests that run in sequence, and then pass or fail. In fact, its more accurate to say each step can pass or fail, and this contributes to an overall pass or fail for that section. You can make the tests as detailed or as general as you wish, but this is in fact an exercise for the tester.

As a developer, you can make the process easier for the tester, saving them a great deal of time. There are certain things that take time and effort when designing an automated test for a page that can a developer might be able to fix up front.

Lets talk about the structure of a possible automated test against an imaginary login form:

  • Main focus of the test
    • Test case 1
      • Stage 1 of the test
      • Stage 2 of the test
      • Stage 3 of the test
      • Test to see if case 1 went well
    • Test case 2
      • Stage 1 of the test
      • Stage 2 of the test
      • Stage 3 of the test
      • Test to see if case 2 went well

This might seem a little generic so lets put some real possible test information into this structure.

  • Describe: Log in As an Admin
    • It: Must fail my login due to an incorrect password
      • Find the username field and type in ‘email@usernametest.com’
      • Find the password field and type in ‘incorrectpassword’
      • Find the login button and click it
      • Does the title contain the word ‘login’?This will pass because we are still on the login page
    • It: Must correctly login
      • Find the username field and type in ‘email@usernametest.com’
      • Find the password field and type in ‘correctpassword’
      • Find the login button and click it
      • Does the title contain the word ‘dashboard’? This will pass because we are on the dashboard

As you can see, marked in bold are the actual tests we perform. They are grouped together so we can understand them better and so that each test makes sense.

You’ll notice that when we set the username, we need to locate the correct field and then instruct the test suite to type in the value. I’ve simplified this somewhat, since we really should also test to see if the value is in the field after we type it, but for simplicities sake, i’ve omitted some best practice for a real test.

So a test has 2 parts,

  1. Find an element ‘find the password field’
  2. Action upon an element ‘type in password’

In the case of the final step

  1. Get the title
  2. Check to see if it has the word dashboard.

We are finding the title and then the action is a comparison.

Finding an element

In automated testing the test engineer will locate the element to interact with using the content of the markup. They can use a number of methods to isolate a single button or other form input.

  • A CSS class such as ‘btn-primary’
  • An attribute such as ‘id’ or ‘data-context’
  • An xpath (or DOM path) such as html/body/div[1]/section/div[1]/div/div/div/form/button

I don’t need to explain how bad the xpath is. If the structure of the page changes in any way, such as the button is moved into another div, or the page is re-worked, then this brittle test will fail. The xpath should only be used by the tester as a last resort when no other method can be employed to locate an element on the page.

The css class is also bad. If a designer or UI/UX person comes along and re-works the page, they may strip out any classes in favour of new improved ones. This would break any tests that rely on css classes in the elements in order to complete properly.

This leaves an attribute such as ‘id’ or even better, ‘data-thing’

Unique Attributes

In the html spec it says:

3.2.3.1 The id attribute
The id attribute specifies its element’s unique identifier (ID). The value must be unique amongst all the IDs in the element’s home subtree and must contain at least one character. The value must not contain any space characters.

This means that in order for a document to be a valid html document the id needs to be unique and this is a good sign. It means that when we select an id on a page it will be unique.

The problem is however that a developer may change this id during development to fit their task at hand. These aren’t really safe to use to find an element either for this very reason. It’s better than the other two however.

If we have no choice but to use the id and we feel comfortable doing this then we can find (for example) all elements with a missing id in our html source using regex like this:

<button ((?!id).)</button>

That leaves data-attributes

W3shools says

The data-* attributes is used to store custom data private to the page or application.

The data-* attributes gives us the ability to embed custom data attributes on all HTML elements.

The stored (custom) data can then be used in the page’s JavaScript to create a more engaging user experience (without any Ajax calls or server-side database queries).

The data-* attributes consist of two parts:

  1. The attribute name should not contain any uppercase letters, and must be at least one character long after the prefix “data-“
  2. The attribute value can be any string

Note: Custom attributes prefixed with “data-” will be completely ignored by the user agent.

Finding these data-attributes in automated testing can be done like this:

<button ((?!id).)</button>

or with xpath like this:

driver.findElement(By.xpath("//*[@data-element='formUsername']"))

The advantage of using xpath like this is we can get an element inside easily.

So if your code looked like this…

<div data-element="formUsername">
<input value="my.username" />
</div>

…you could get it like this:

driver.findElement(By.xpath("//*[@data-element='formUsername']/input"))

To search your source HTML for (example) button elements that are missing the data-element attribute you could do this:

<button ((?!data-element).)</button>

With that mind here are some best practices for deb developers to help testers to test with automation:

  • Where possible, use a data-element attribute in your code with a value unique to the application, not just the page or component you are in.
  • In the documentation for the component, give the data-element names and the element they are connected to in the comments or docs for the project. This stops a tester wasting time searching your source to get an element.
  • Though it’s likely that your development story was driven by specific fields and what their behaviours are, of which testers are likely aware before testing, its very possible you have introduced buttons and tabs and such beyond the story that testers are not aware of. Explain any new features (if any) that you added to the component or page that breaks their original test scripts.
  • Realise that your goal is not get this work back with bugs in it. If you are tasked with fixing a bug, or adding a feature to an element in an application, run any existing tests yourself to ensure that everything will works before you pass it back to QA.