понедельник, 13 мая 2013 г.

Автоматический поиск поля ввода на странице

Идея написать искусственный интеллект не может не цеплять.
Это сложная задача, которую сегодня мы ещё не готовы решить. Но добавить немного "мозгов" в наших роботов, скрипты и автоматизированные тесты мы можем.

Проекты бывают разные. Некоторые веб формы содержат сложные элементы, некоторые формы очень просты.
Когда мы вновь и вновь определяем локаторы полей, которые имеют прикрепленные к ним надписи, невольно возникает мысль - эй, а почему автоматические тесты не могут сами понять куда надо ввести значение?

Вот поле, над ним надпись: Имя. Почему бы не писать в тесте "Ввести в поле Имя значение "Вася"", а задачу найти это поле, определить его идентификатор и ввести значение в него предоставить автоматизированным тестам?
Вместо этого мы тратим свое время, усердно записывая идентификаторы элементов (и даже придумываем множество способов делать это разными путями)

(спойлер: пример кода на Python прилагается)

Я сейчас как раз разрабатываю систему, цель которой - позволить писать тесты "с нуля", на новом проекте, новичкам. Сразу начинаешь писать тесты в виде историй использования (user story). А код не пишешь, он уже за тебя написан и проверен. В нём уже применены лучшие практики и шаблоны. Тебе остаётся описать логику тестируемого приложения, ты занимаешься высокоинтеллектуальным трудом, а рутина остаётся программе.

Было бы здорово находить элементы автоматически, пусть - не всегда это возможно, но в самых простых случаях - почему бы и нет.


from bs4 import BeautifulSoup


class ArtificialIntelligence:
    """
        This class allows to find input and select controls
        without manual input of identificators.
        We can find input fields by labels near those fields
    """


    def __init__(self, driver):
        self.driver = driver

    def _get_xpath_of_element(self, element):
        " this funcion allows to get xpath of soup elements "

        xpath = element.name

        for parent in element.findParents():
            if parent.name != '[document]':
                k = 0
                for tag in parent.find_previous_siblings():
                    if tag.name == parent.name:
                        k += 1
                if k == 0:
                    xpath = parent.name + '/' + xpath
                else:
                    xpath = parent.name + '[' + str(k+1) + ']/' + xpath

        xpath = '/html/body/' + xpath
        return xpath

    def find_element(self, label, element_type='input'):
        element = None

        " get html code of page "
        page = self.driver.find_element_by_tag_name("body")
        html = page.get_attribute("innerHTML")
        html = str(html.encode("utf-8", "replace"))

        " load html to soup structure for parsing "
        soup = BeautifulSoup(html)

        " search label in html code "
        label_element = soup.find(text=str(label))

        " search input element after the label"
        input_element = label_element.parent.find_next(element_type)

        " return xpath of input element "
        xpath = self._get_xpath_of_element(input_element)
        element = driver.find_element_by_xpath(xpath)
        return element


Это действительно "магия" - оно работает!
Теперь, если я вижу форму, на которой к каждому полю ввода прилагается подпись, я представляю, как заполнение этой формы можно автоматизировать, не узнав ни одного локатора.
Искусственный интеллект "на коленке". Конечно, это самый элементарный алгоритм, который будет работать не всегда, но его можно легко усовершенствовать, чтобы он находил нужные поля ввода и выпадающие списки в нужном нам приложении.

Такой "интеллект" мы всё ещё вынуждены совмещать с явным указанием идентификаторов для элементов, которые не могут быть найдены этим алгоритмом (кнопки, картинки, ячейки таблицы и тому подобное - кому я рассказываю, вы же всё знаете).
Это - первые шаги к системе "будущего", когда задачу фильтрации информационного шума мы сможем передать компьютерам. И уже сегодня мы готовы применять такие техники, так почему мы всё ещё тратим время на описание идентификаторов?

Кстати, идея такой идентификации не нова, вот, например, скоро будет доклад Андрея на конференции Auto ConfetQA: ссылочка на описание доклада (с нетерпением жду этого доклада, чтобы обменяться опытом и позаимствовать интересные идеи для своего проекта :) )