company_banner

Эксперимент в Яндексе: как роботы помогают тестировать сервисы

    Робот не может причинить вред человеку
    или своим бездействием допустить,
    чтобы человеку был причинён вред.
    Айзек Азимов

    Я занимаюсь в Яндексе тестированием и хочу рассказать вам об одном нашем экспериментальном проекте. В его рамках мы создали полностью автоматический инструмент для функционального тестирования веб-интерфейсов — Роботестер. Наш робот удовлетворяет первому закону роботехники: он не причиняет вред человеку и не бездействует, когда находит баг, который может создать людям проблемы.

    Роботестер сам ищет ошибки

    Мы подумали, что сервисом наравне с людьми может пользоваться какое-то количество тестовых роботов, которые оперативно и чётко будут сообщать о его проблемах. Роботы никогда не отдыхают и делают так, чтобы с появившейся ошибкой столкнулось как можно меньше людей.

    Нам такая идея очень понравилась, и мы начали её реализовывать.

    О функциональном тестировании


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

    Чтобы поставить себя на место высококвалифицированного ручного тестировщика, откройте страницу расширенного поиска Маркета. Начните выбирать себе ноутбук, проверяя при этом, что нигде не съезжают блоки верстки, все элементы страницы подгружаются, а после заполнения формы вы попадаете на страницу со списком ноутбуков, а не получаете 404 ошибку. Такой вид ошибок мы назывем общими. А теперь представьте, что вам нужно проверить на них все сервисы Яндекса. Становится жутко.

    Но 404 и в Африке 404, а js ошибка, на каком бы сервисе она ни возникла, – это ошибка, NaN не может быть ни ценой товара, ни расстоянием от пункта А до пункта Б. Этот класс ошибок достаточно однообразен для всех сервисов. Процесс их тестирования можно и нужно автоматизировать, а затем — масштабировать, что сделать с квалифицированными кадрами очень сложно.

    Частично справиться с такими проблемами могут автотесты: написав однажды, их можно запускать, когда хочешь. Но автотесты нужно не только писать, но и поддерживать. И мы решили пойти дальше и ручного тестирования, и автотестов.

    Робота, который должен сам тестировать сервисы, нужно было научить следующему:

    1. Обходить сайты как по ссылкам, так и при помощи форм.
    2. Распознавать элементы страницы и взаимодействовать с ними.
    3. Генерировать и выполнять тестовые сценарии.
    4. Отлавливать ошибки.


    Обход сайтов


    Этот пункт тесно связан с распознаванием элементов: если мы не поймём, что в текстовое поле надо вводить название города, то с большой вероятностью не сможем перейти на следующую страницу и полноценно обойти весь сайт. В тестировании для работы с формами хорошо известны такие технологии, как WebDriver, PhantomJS и т.д.

    Стоит сказать, что существует всего один краулер, который умеет обходить ajax-сайты, — CrawlJax. Он оперирует со всеми элементами, но совершенно не разбирается в них, поэтому для хоть немного сложного сайта работает безумно долго. Мы сначала хотели сделать «мозг» для этого робота, но поняли, что его придётся значительно переделывать под наши нужды. Поэтому решили сделать свой продукт.

    Мы используем как «статический» краулинг, так и «динамический». В первом случае мы устанавливаем нужные куки (регион, userid) и ходим по ссылкам. Во втором — взаимодействуем с формой, переходим на другую страницу или в другое её состояние, которое получается в результате активации нового поля ввода.

    Распознавание элементов страницы


    Робот должен моделировать действия пользователя, поэтому ему необходимо взаимодейстовать с элементами веб-страниц также, как это делают люди. Если перед ним, скажем, выпадающий список, то проблем нет, — ему надо выбрать один из вариантов. Также проблем не будет и с checkbox и radio button. Сложности возникают с полями ввода двух типов:

    1. Сложные поля ввода с JavaScript;
    2. Поля текстового ввода.

    Пока наш робот не умеет взаимодействовать с произвольным полем, которое обрабатывается JavaScript, так как в таком поле может быть «зашита» довольно сложная логика. Нам нужно было научить робота заполнять поля для выбора даты, и мы создали довольно универсальный модуль для взаимодействия с такими полями. Теперь Роботестер умеет выбирать даты на всех страницах Яндекса.

    На втором пункте хотелось бы остановиться подробнее. Допустим, перед нами поле текстового ввода. Ясно, что есть поля, куда можно вводить практически любой текст (например, строка поиска на ya.ru). Но во многих случаях ввод произвольного текста приводит к тому, что значительная часть функциональности не используется. Введя некорректные данные, в некоторых случаях робот не сможет не то что перейти на следующую страницу, но и активировать все поля ввода на текущей.

    Например, рассмотрим форму поиска рейсов на странице Яндекс.Расписаний. Если по каким-то причинам робот решит, что в первое поле надо вводить какой-то популярный поисковый запрос, а во второе – название определенной профессии, то ничего хорошего из этого не выйдет.

    Ошибка при вводе данных в поля формы

    Если в поисковой форме робот совершит подобную ошибку, то мы никогда не увидим (и никогда не проверим) результаты поиска. Если в форме регистрации – никогда не попадем на страницу успешно зарегистрировавшегося пользователя и т.д. Более того, если поля заполнены неправильным образом, в некоторых формах даже не получится нажать кнопку, кликнув по которой челоек должен попасть на новую страницу или увидеть старую в новом состоянии.

    Как же робот может понять, что нужно вводить в определённое поле ввода? Подобные задачи возникают также при разработке поисковых роботов, который индексируют deep web. Термин «deep web» используется для обозначения той части информации в интернете, которая «скрыта за формами» — для доступа к ней необходимо заполнить некоторую поисковую форму. Существует несколько подходов к решению этой задачи, прочитать о них можно, например, здесь, здесь или здесь.

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

    С другой стороны, для поискового робота надо всего лишь максимизировать распознавание полей. Уже какие-то результаты поиска можно будет получить, если в форме будет правильно распознано 90% полей. Но нас не устроит уровень распознавания ниже «абсолютных» 100%. К тому же, мы работаем не только с формами поиска. Следовательно, для того чтобы создать робота, который будет качественно имитировать поведение человека на странице и присылать корректную информацию, нам надо было разработать как можно более точный алгоритм распознавания. Универсальностью мы готовы были пожертвовать.

    Наш алгоритм строится на словаре типов. Словарь выглядит следующим образом:

    Алгоритм распознавания сайтов, строящийся на словаре типов

    В итоге распознавание типа поля сводится к выбору одного из готовых типов, содержащихся в словаре (на данный момент у нас их 27). Для этого для каждого типа ввода мы сформировали список ключевых слов. В их список были внесены те, которые, присутствуя рядом с данным полем в интерфейсе, повышают вероятность того, что оно относится к данному типу. Разумеется, не все ключевые слова одинаково полезны. Каждому ключевому слову сопоставлен вес, но об этом позже. Алгоритм можно разделить на несколько частей.

    Сегментация. Делим интерфейс на части, соответствующие полям ввода.

    Сегментация поля ввода

    В нём нас интересует текст и его расположение относительно того поля ввода, которое мы изучаем. Но, во-первых, не очень понятно, что такое «текст» — только видимого текста может быть недостаточно, а сразу весь текст верстки брать плохо, потому что он может содержать в себе много мусора. Поэтому мы используем и тот, и другой текст, но с разными весами.

    Задачу извлечения соседнего (видимого или невидимого) текста мы решаем так:

    • element.innerHTML+= «@#@$@#!»
    • извлекаем (видимый/невидимый) текст из всей формы
    • element.innerHTML-= «@#@$@#!»

    Дальше находим в полученном тексте подстроку «@#@$@#!». Она и будет символизировать позицию нашего поля ввода в полученном тексте. Текст мы извлекаем при помощи jQuery.

    Проставление весов. Полученный текст разбивается на слова, после чего у каждого слова появляется вес. Он зависит от того, видимо слово или нет, и от расстояния между ним и полем ввода.

    Выбор типа. Ключевые слова для типа «price» в обилии находятся рядом с полем ввода цены.



    Алгоритм оценки релевантности типа выглядит следующим образом. Сначала слова упорядочиваются по весу в убывающем порядке. Назовем этот список списком Х. Теперь будем считать веса уже самих типов полей. У каждого типа есть свой набор слов с весами — это будет список Y. Пересечем этот набор со списком Х и получим список Z. Веса слов в списке Z будут получены перемножением весов в списках X и Y. Дальше нам нужно избавиться от ситуаций, в которых на вес типа влияют «далекие» слова — находящиеся в другой части страницы. Поэтому давайте еще слово в списке Z с номером i разделим на 2i, после чего сложим все веса слов в списке Z – это и будем считать весом данного типа.

    Как уже было отмечено, важный элемент этого метода — набор ключевых слов с весами для каждого типа. Веса необходимо выбирать так, чтобы максимальный суммарный вес оказывался у «правильного» типа поля. Как подобрать такие веса? Тут существует множество способов, мы выбрали простую «подгонку». То есть задали веса некоторым способом, а затем корректировали их в ситуации, когда робот неправильно распознавал тип какого-то поля, которое нам было необходимо обработать. Дл обучения робота был создан специальный интерфейс, и, если оператор указывал, что робот ошибочно выбрал тип «Цена», основываясь на слове «от» (а надо было выбирать тип «Адрес» основываясь еще и на слове «маршрут»), то вес слова «от» для типа «Цена» уменьшался, а вес слова «маршрут» для типа «Адрес» увеличивался.

    Однако универсальность такого подхода находится на довольно низком уровне. При увеличении числа тестируемых сервисов неизбежно возникнет ситуация, когда мы не сможем вручную подобрать веса, чтобы они работали на всех сайтах. Тогда придет время придумывать что-то новое: например, использовать машинное обучение, основываясь на «правильных» типах полей, заданных оператором для некоторых страниц. Собственно, изначально мы и не думали, что такой простой алгоритм нас устроит, но, как оказалось, он полностью подходит под наши нужды — в данный момент робот успешно распознает все поля всех форм, для которых мы его запускаем. Это 5-6 различных сервисов Яндекса.

    О результатах


    Роботестер помогает тестировщикам Яндекс.Маркета уже более пяти месяцев. 12% багов, найденных за это время на production среде, принадлежат роботу. Он не спит, не ест (аппаратное время-то он кушает), а главное – он один, но его много. Спасибо 32 ядрам! Хочется верить, что и в тестировании рутинный ручной труд будет заменяться механическим.

    Конечно, такие роботы не отнимут труд у ручных тестировщиков и автотестеров, потому что в бизнес-логике и специфике сервиса разобраться бывает сложно даже человеку. Просто в работе квалифицированных кадров станет меньше рутины, а в интернет-сервисах — меньше ошибок.

    Во время работы над роботом у нас возникло немало других интересных задач. Например, пришлось придумать свой алгоритм для краулинга, так как за разумное время он не справлялся с обходом мобильного сайта Маркета, содержащего десятки миллионов страниц. Или, например, робот выдавал очень много похожих сообщений об ошибках. Мы разработали специальную систему отчетов, в которых похожие ошибки «склеиваются» в одну. Есть также интересные задачи, до которых пока «не дошли руки» — например, как отличать опечатки в пользовательских комментариях от функциональных ошибок, заметных в тексте. Обо всем этом мы расскажем в следующих сериях.
    Метки:
    Яндекс 607,65
    Как мы делаем Яндекс
    Поделиться публикацией
    Комментарии 28
    • 0
      Спасибо, интересно было почитать.
      А сколько по времени делали робота?
      • +7
        Всего разработка заняла полтора года. Мы проводили эксперименты, сделали много лишних действий. Думаю, зная то, что мы знаем сейчас, можно было бы написать за пару месяцев.
      • +2
        Ваш робот похож на миньона. Гадкий Я(ндекс)?
        • 0
          Действительно, внешнее сходство есть.
          • 0
            А я то думал, что это и есть миньон из мультика, пока комментарий не прочитал :)
          • +3
            Планируете ли вы развивать этот «эксперимент» в открытом сообществе?
            • +8
              Очень не хочется обещать и не выполнить! Планы такие есть, да.
          • 0
            А в чем состоит самообучаемость робота? Я так понимаю, именно в этом состоит различие между автотестами и роботом. Или же разница сводится лишь к тому, что вам не надо жестко прописывать страницы/формы в сценариях, он способен сам их находить и обрабатывать?

            Труд в любом случае большой, и вам респект, просто хотелось бы прояснить этот момент.
            • 0
              У робота самообучаемости нет. Да, универсальность заключается в том, что роботестер самостоятельно выбирает способ взаимодействия с формой и генерирует тестовые сценарии без участия человека. Планируем добавить supervised-методы обучения, например, для классификации страниц с ошибками. Unsupervised-методы для обнаружения ошибок — интересная тема, но к роботу в данный момент отношения не имеет.
            • 0
              А могли бы рассказать, а лучше и привести пример лога?
              Интересно какие данные вы получаете на выходе и как в дальшем их обрабатываете.
              • +3
                Результат работы робота — сообщения об ошибках. Лог мы используем только для отладки.
                Роботестер выполняет тест-кейс, а потом собирает со страницы различную информацию (текст со страницы, ссылки, текст алертов(если они появились) и отдает эту информацию в JUnit-тесты. JUnit-отчеты нам не совсем подходят, поэтому мы строим из них свои, «красивые» отчеты. Их смотрит тестировщик и пишет баг-репорты. В «красивых» отчетах можно применять фильтры по типу ошибок и склеивать похожие сообщения об ошибках в группы.
              • –2
                Бедные тестировщики потеряют свои рабочие места :(
                • +4
                  богатые (=квалифицированные) — не потеряют ;)
                  • +1
                    Вы правы! Например, с бизнес-логикой даже человеку бывает непросто разобраться.
                  • +3
                    Не потяреют. Наоборот, мы планируем улучшить жизнь тестировщиков, отдав рутинную работу роботестеру.
                  • 0
                    С формами вроде бы напрашивается решение встроить доп. семантику в сам HTML-код. Например, [input … name=«address» data-ya-type=«geo-cityname-destination»]. Ну или что-то вроде. Ведь и робот и тестируемые страницы у вас под контролем.
                    • 0
                      Хорошая мысль, мы думаем сделать что-то вроде «микроразметки для тестирования». Конечно, непросто внедрить общий формат в большой компании и добавить такую разметку в уже существующие интерфейсы.
                      • +1
                        Все бы хорошо, только вот заставить всех писать такую разметку очень сложно.
                        Можно пойти с другой стороны и парсить label'ы и placeholder'ы. А дальше по набору эвристик определять тип поля. Дополнительно можно попробовать парсить .error, .validate — поля. Взять наиболее популярные фреймворки, посмотреть, какого вида валидацию они используют и при неправильном вводе смотреть, что вернула валидация. Вот тут масса простора для самообучения робота, хотя и возникает необходимость работы с естественными языкам.
                        • +1
                          Да, мы используем label, placeholder, name — все что может содержать текст, несущий информацию о типе поля ввода. Парсить валидацию — это интересно! Кстати, в одной статье про краулинг deep web описан примерно такой способ: робот заполняет форму, отправляет ее и смотрит какой контент пришел в ответ. Так он делает много раз. Если очередной вариант заполнения практически не изменяет контент ответа, значит форма была заполнена некорректно.
                      • +1
                        Может исходный код откроете? Я понимаю, что робот приспособлен к сайтам яндекса, но думаю другим будет легче приспособить под себя, чем писать свой
                        • 0
                          У нас есть желание сделать этот проект открытым, но в данный момент мы не готовы говорить об этом. Причину описывал выше.
                        • +4
                          Очень интересно!

                          Интеграционные тесты (цель которых — иметь большое покрытие и, грубо говоря, отлавливать 404) в UI действительно могут быть автоматизированы влоть до автогенерации множества сценариев.

                          Текстовые поля (в том числе с числами) можно заполнять одной из подсказок, всплывающей после ввода одной буквы/цифры или пары символов. Правда возникает требование, иметь такие подсказки для все текстовых полей, но это требование вполне разумно и не повредит юзабилити. Для особых текстовых полей, где подсказки вредят юзабилити, можно договорится с разработчиками, что подсказки возникают только для определенного юзерагента. Разработка подсказок для каждого поля проще, тем поддерживать руками интеграционные тесты.

                          Ну и, конечно, как уже сказали, можно заставить разработчиков давать подсказки роботу через семантику HTML. Семантика кнопок Вперед, Назад, Расширенные настроки, например, позволит роботу не гулять по циклу в длиных визардах, а веса у ссылок и значений позволят в первую очередь покрывать типичные сценарии).

                          Разработчикам больше работы, зато тестеры будут заниматься только функциональными тестами бизнес логики.
                          • 0
                            Видимо, до Яндекс.Погода у робота еще не дошли руки(
                            image
                            • 0
                              Не открывается картинка
                              • 0
                                Прошу прощения, хабрасторадж 407 ошибку мне выдавал, вот она
                            • 0
                              Интересно, спасибо. В хромиуме и firefox 19.0.2 не воспроизводится, кстати. Какая у вас версия браузера?
                              • 0
                                FireFox 20.0.1 под Win7 Pro. Сегодня у меня уже тоже не воспроизводится =(

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое