icon-arrow icon-check icon-mail icon-phone icon-facebook icon-linkedin icon-youtube icon-twitter icon-cheveron icon-download icon-instagram play close icon-arrow-uturn icon-calendar icon-clock
Werken bij Whitehorses
Blog 02/11/2015

Testing ADF with Selenium: Accessing elements through multiple regions

Solution

Integratiespecialist
Mike Heeren /
Integratiespecialist

During the project where I am currently working on, we use Selenium for (unit)testing the ADF user interfaces of the application. Most pages of our application contain multiple (nested) regions with elements that need to be filled or clicked using Selenium. One of the problems we ran into was that ADF does not always generate the exact same ID for an element at runtime, especially when the element is a child of multiple nested regions. In our case it was not possible to search the elements using a “simple” XPath expression like “//input[contains(@id,’it1′)]” because some IDs can be used in multiple active regions.

We were eventually able to solve our issues not by trying to access the elements by their IDs, but using XPath expressions that search through the multiple layers of the generated HTML elements. I have written a Java utility class that can be used to achieve this. This Java utility class uses the DIV components generated for each region. The IDs of this DIV end with a colon and the ID given in the ADF page/fragment for the region. With this knowledge it is possible to generate an XPath expression which “steps through” the different layers of regions in the ADF page. The final problem that occurred was that XPath does not contain an ends-with function. The private constructEndsWithXpath method in the utility class can be used to achieve the same result as expected from an ends-with function.

package nl.whitehorses.selenium;
	 
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
	 
public class AdfComponentUtils {
	 
    private AdfComponentUtils() {
        super();
    }
	 
    public static WebElement findWebElement(WebDriver driver, String elementId, String... parentIds) {
        return driver.findElement(By.xpath(constructFindElementXpath("@id", new StringBuilder(":").append(elementId).toString(), parentIds)));
    }
	 
    private static String constructFindElementXpath(String locatorType, String locatorValue, String... parentIds) {
        reverseStringArray(parentIds);
	
        StringBuilder xpathBuilder = new StringBuilder();
	 
        if (parentIds != null) {
            for (String regionId : parentIds) {
                xpathBuilder.append("//*[").append(constructEndsWithXpath("@id", new StringBuilder("':").append(regionId).append("'").toString())).append("]");
            }
        }
	 
        xpathBuilder.append("//*[").append(constructEndsWithXpath(locatorType, new StringBuilder("'").append(locatorValue).append("'").toString())).append("]");
	
        return xpathBuilder.toString();
    }
	 
	 
    private static String constructEndsWithXpath(String string, String suffix) {
        return new StringBuilder("substring(").append(string).append(", string-length(").append(string).append(") - string-length(").append(suffix).append(") + 1) = ").append(suffix).toString();
    }
	 
    private static void reverseStringArray(String... array) {
        for (int i = 0; i < array.length / 2; i++) {
            String temp = array[i];
            array[i] = array[array.length - 1 - i];
            array[array.length - 1 - i] = temp;
        }
    }
}

The findWebElement method can be used to find the ADF elements in the Selenium tests:

  • Driver: The WebDriver instance that has to be used to find the elements.
  • ElementId: The ID that is assigned in the ADF pages/fragments to the element, e.g. soc1it1cb1.
  • ParentIds: Optional parameter(s) for providing the parent element(s) of the element you are looking for.

The method “AdfComponentUtils.findWebElement(<driver>, “select”, “soc1”, “r3”, “r2”, “r1″);” could be used when the structure of the ADF page is something like:

<page>
    <region id="r1">
        <region id="r2">
            <region id="r3">
                <select id="soc1">
                    <option value="1">Test 1</option>
                    <option value="2">Test 2</option>
                    <option value="3">Test 3</option>
                </select>
            </region>
        </region>
    </region>
</page>

Note: One last tip, for some ADF components, such as SelectBooleanCheckbox components, a suffix is appended to the ID generated at runtime. ::content will be appended to SelectBooleanCheckboxcomponents for example. Because we are using ­­ends-with, you should keep this in mind while using the findWebElement method. When this is the case, a statement like the following can be used: “AdfComponentUtils.findWebElement(<driver>,”input”, “sbc1::content”);”.

Good luck testing your ADF application using Selenium!

Update 05-11-2015: Like Wilfred van der Deijl mentions in the comments, is SeleniumTools an excellent toolkit for testing ADF applications using Selenium. However, we were not able to use the SeleniumTools in our project because SeleniumTools is developed for ADF 12c. In our project we have to test an ADF 11g application.

I have also made a few small modifications on the “AdfComponentUtils” class, which make it easier to use. The “elementType” parameter is removed so it is easier to write tests because you no longer have to know which HTML element is generated for a component.

Check out this relatable blogpost:

Geen reacties

Geef jouw mening

Reactie plaatsen

Reactie toevoegen

Jouw e-mailadres wordt niet openbaar gemaakt.

Geen HTML

  • Geen HTML toegestaan.
  • Regels en alinea's worden automatisch gesplitst.
  • Web- en e-mailadressen worden automatisch naar links omgezet.
Integratiespecialist
Mike Heeren /
Integratiespecialist

Wil je deel uitmaken van een groep gedreven en ambitieuze integratiespecialisten? Stuur ons jouw CV!