DEV Community

Cover image for Constructing Custom Locators in OpenTest
Sam E. Lawrence
Sam E. Lawrence

Posted on

Constructing Custom Locators in OpenTest

On a previous project, I got to use a nifty tool called OpenTest to orchestrate test automation on an Android app. It's not a widely popular framework yet, but I hope it grows because it solves one particular problem quite well. OpenTest enables a QA team to write tests once that can run across multiple environments and platforms. It's a solid tool if you're looking to do something like automate an iOS and Android app that have identical UIs, or just want a shared language for your web and mobile app testing frameworks. OpenTest is also written in YAML, so it's approachable for even quite junior SDET teams, and it contains a lot of keyword-based actions that make UI tests simple to compose.

In most cases, you simply structure tests like this:

- description: Verify Text is Accurate
  action: org.getopentest.appium.AssertElementText
  args:
    locator: $data("locators/view").area.element
    text: 'Whatever text I expect to see'
Enter fullscreen mode Exit fullscreen mode

In most cases, you can locate elements by their id or text properties, but in cases where you need to construct custom locators on the fly, it can be useful to know how OpenTest interprets Javascript.

Javascript can be dynamically inserted into OpenTest tests using the script tag, and if a bar (|) is inserted on the first line, then everything following it is interpreted as Javascript, so you can use multi-line Javascript blocks. For more on scripting support in OpenTest, read the docs.

For now, all you need to know is that Javascript can be dynamically inserted anywhere in a test, and that you can call all your basic OpenTest actions like AssertElementText by using the special $runAction command in your Javascript blocks.

Let's imagine an example interface for an administrator viewing an Employee Management System that lists employees and has a "Reset Password" button next to their names. Let's say there are just two employees in the system, Taz and Julia.

In this example, we have locators in our OpenTest /data repository that correspond to the elements in the interface.

Taz and Julia show up in my Employee Management System, and next to each of their names, I can see a button to reset their passwords. We're going to have two separate locators for the two "Password Reset" buttons. To test this, I need to find the row with their name in it, and then find the corresponding password reset button to select. We can use string concatentation to directly insert each person's name into the xpath locator.

tazBtn:   { xpath: "//ClassName[@resource-id='personRow' and @text='Taz']/following-sibling::android.widget.Button[@resource-id='passwordResetButton']" }
juliaBtn: { xpath: "//ClassName[@resource-id='personRow' and @text='Julia']/following-sibling::android.widget.Button[@resource-id='passwordResetButton']" }
Enter fullscreen mode Exit fullscreen mode

OpenTest does a good job of separating test logic from test data. References to data strings, numbers, and other values can be included in tests using references formatted like: $data("fileName").parent.child.

Here's an example of declaring a text label directly, and then using the value concatenated into a locator identifier.

- description: Verify Text Label
  script: |
    var label = "Some text.";
    $runAction("org.getopentest.appium.AssertElementVisible", 
    {
    locator: "//android.widget.TextView[@text='" + label + "']"
    });
Enter fullscreen mode Exit fullscreen mode

It's important to remember that direct string concatenation will not work in your locator strings, you'll need to explicitly declare a new variable for the string you'll use at the top of our Javascript block.

Here's an example that shows how to structure some code that relies on inserting a $data value into a text property so we can type it into a field. In this case, we're pulling from a list of products in our $data directory.

- description: Enter product name into text field
  script: |
      var textToEnter = $data("products").product1.name
      $runAction("org.getopentest.appium.SendKeys", {
        locator: $data("locators/form").input,
        text: textToEnter
      });
Enter fullscreen mode Exit fullscreen mode

You might be tempted to define your text value directly with the $data value, but this won't work and you must explicitly define that value first in a variable at the top of your Javascript block.

I hope this saves someone a bit of frustration. If you're using OpenTest on a project, I'd love to hear what you think of it on Twitter.

Discussion (0)