Уровни абстракции. Создание кастомных элементов.

Уровни абстракции

В автоматизации часто применяются следующие абстракции:

  • Page Object
  • Page Element

Паттерн Page Object хорошо зарекомендовал себя в автоматизации. Основная идея – инкапсулировать логику поведения страницы в классе страницы. Таким образом, тесты будут работать не с низкоуровневым кодом, а с высокоуровневой абстракцией.

Плюсы Page Object:

  • Разделение полномочий: вся логика страницы описывается в Page Object классах, а тестовые классы лишь используют их публичные методы и проверяют результат.
  • DRY – все локаторы помещаются в одном месте
  • Инкапсуляция работы с драйвером. Полезно при кросс-браузерном тестировании
  • Page Objects позволяет записать локаторы в декларативном стиле

В классическом варианте, паттерн предполагает создание одного класса на одну страницу. Это может быть неудобно в ряде случаев:

  • Использование кастомных элементов при создании веб-приложений
  • Присутсвие кастомных элементов на многих страницах

В решении этой проблемы может помочь использование наследования, но агрегация видится предпочтительнее. Поэтому лучше воспользоваться паттерном – Page Element. Page Elements – позволяет дробить страницу на более мелкие составляющие – блоки, виджеты и т.д. После чего эти блоки можно переиспользовать в нескольких страницах.

Кастомные элементы

WebDriver очень мощный инструмент, который позволяет выполнять различные действия над элементами страниц. Для этого используется класс WebElement. Но, как и всегда, не все так просто и замечательно. На практике автоматизаторы сталвкиваются с тем, что разработчки используют кастомные элементы, с которыми класс WebElement не работает. К примеру, возьмем написание тестов для таблиц. Стандартные классы WebDriver не работают с таблицами, потому нам нужно написать свой. В котором мы опишем работу с элеметами таблицы (ячейки, строки и т. д.).

import java.util.List;

public class WebTable {
    private WebElement _webTable;

    public WebTable(WebElement webTable)
    {
        set_webTable(webTable);
    }

    public WebElement get_webTable() {
        return _webTable;
    }

    public void set_webTable(WebElement _webTable) {
        this._webTable = _webTable;
    }

    public int getRowCount() {
        List<WebElement> tableRows = _webTable.findElements(By.tagName("tr"));
        return tableRows.size();
    }

    public int getColumnCount() {
        List<WebElement> tableRows = _webTable.findElements(By.tagName("tr"));
        WebElement headerRow = tableRows.get(0);
        List<WebElement> tableCols = headerRow.findElements(By.tagName("td"));
        return tableCols.size();
    }

    public WebElement getCellEditor(int rowIdx, int colIdx, int editorIdx) throws NoSuchElementException {
        try {
            List<WebElement> tableRows = _webTable.findElements(By.tagName("tr"));
            WebElement currentRow = tableRows.get(rowIdx-1);
            List<WebElement> tableCols = currentRow.findElements(By.tagName("td"));
            WebElement cell = tableCols.get(colIdx-1);
            WebElement cellEditor = cell.findElements(By.tagName("input")).get(editorIdx);
            return cellEditor;
        } catch (NoSuchElementException e) {
            throw new NoSuchElementException("Failed to get cell editor");
        }
    }

    public WebElement getCellEditor(int rowIdx, int colIdx, int editorIdx) {
        List<WebElement> tableRows = _webTable.findElements(By.tagName("tr"));
        WebElement currentRow = tableRows.get(rowIdx-1);
        List<WebElement> tableCols = currentRow.findElements(By.tagName("td"));
        WebElement cell = tableCols.get(colIdx-1);
        WebElement cellEditor = cell.findElements(By.tagName("input")).get(0);
        return cellEditor;
        }
}