В прошлом посте мы начали рассказ о нашей оптимизации чекаут-процесса для Crocs и ознакомились с самыми широко распространенными вариациями тестирования онлайн ресурсов: А/Б и многовариантным тестированием. Стоит отметить, что в случае e-commerce основой успеха в этой части является возможность эффективного тестирования всего сайта и отдельных его элементов. В свою очередь, возможность тестирования и улучшения результатов тестирования напрямую зависит от технических возможностей отдельного взятого веб-ресурса. В проектах с большим оборотом функционал тестирования становится архиважным. Сегодня же мы расскажем об организации фреймворка для автоматизированного регрессионного тестирования, который был внедрен для Crocs, об использованных при этом ключевых технологиях, и о реализации самого кода.
Организация фреймворка для автоматизированного регрессионного тестирования:
Для реализации проекта был использован фреймворк Thucydides+JUnit. Именно это решение позволило нам обеспечить максимальную гибкость настройки, внедрить множество вспомогательных методов для работы с веб-элементами, предусмотреть логирование и обработку исключительных ситуаций, и, конечно же, создание красивых отчетов.
На данном этапе мы смогли покрыть следующие локализации сайтов Crocs: США, Канада, Европа, Австралия, Тайвань, Великобритания, Польша, Германия, Корея, Япония, Сингапур, Гонконг, Финляндия, Франция и Голландия.
Так как функционал отдельных локализаций сайтов отличается, количество тестов для сайтов также варьируется. В рассматриваемом случае, минимальное количество составило 70, максимальное — 101 тест.
Среда непрерывной интеграции: Jenkins.
Ключевые технологии, которые используются во фреймворке:
1. Как правило, end-to-end автоматизированное тестирование пишется отдельно для одного конкретного сайта. Множественные отличия в дизайне и логике работы сайта требуют большого количества развилок в коде, что приводит к плохой его читаемости. Поэтому для реализации идеи “один тест должен выполняться на разных локалях и версиях сайта” мы решили использовать рефлексию (механизм исследования данных о программе во время её выполнения, осуществляется с помощью Java Reflection API, который состоит из классов пакетов java.lang и java.lang.reflect). Вот как это можно сделать:
Предположим, что есть некий тест. На высоком уровне тест представляет собой последовательность шагов, которые должны быть выполнены вне зависимости от тестируемого сайта. Сайт определит особенности реализации каждого шага.
Сайт, на котором он будет выполняться, зависит от значения системного свойства site. Это свойство в Java передается как параметр командной строки. Для обращения к нему используется строка кода System.getProperty(“site”); Таким образом, необходимо реализовать коммутацию шагов goHome, goToSite, login и complexStep в зависимости от сайта. Пусть все шаги – это методы класса AllSteps.
Этот класс – библиотека шагов Thucydides. Прежде всего, он содержит ссылки на конкретные реализации шагов. Предположим, что конкретных сайтов три: Корея, Тайвань и США. Тогда
public class AllSteps extends ScenarioSteps {
}
Сердце класса AllSteps — это метод, который называется reflector (отражатель). Его задача – перенаправление выполнения шага от общей библиотеки шагов к одной из трех специфических. В обычной ситуации метод через точку вызывается у объекта. Однако в Java реализован и другой способ сделать то же самое, то есть вызвать объект через точку у метода. Данное действие реализовано в java.lang.reflect.Method. Имя вызываемого метода превращается в строку, что позволяет производить с этим именем разные манипуляции. Предположим, что метод, реализующий шаг для некого сайта, называется так же, как и сам шаг, но с добавлением названия сайта в конец имени метода. То есть login превратится в loginUS, goHome в goHomeUS и так далее.
Для случая с тремя сайтами отражатель может быть реализован так:
Главная особенность (и неудобство) заключается в том, что для подобного «перевернутого» вызова метода необходимо создать специальную структуру данных, которая будет соответствовать сигнатуре метода. В приведенном примере это передаваемая снаружи переменная types, которая формируется методом-оберткой, вызываемым из теста, расположенного на самом верхнем уровне.
Параметр types будет всегда, даже если сигнатура вызываемого метода пуста (в этом случае ему нужно присвоить null или new Class[0]). Значения параметров шага передаются внутрь отражателя без изменений. Так как параметров может быть переменное количество, рефлектор также объявлен как метод, принимающий переменное количество параметров Object.
Все шаги обертки, реализующие обращение к отражателю, должны быть помечены аннотацией
Для вывода в лог ошибок отражателя используется метод error, который также является шагом Thucydides. Он ничего не делает, но его параметры (сообщение об ошибке) будут попадать в отчет.
Рефлектор переадресует вызов в библиотеку шагов соответствующего сайта. Типичный шаг занимается тем, что переадресует вызов на PageObject, который будет работать со страницей через веб-драйвер. Библиотеки PageObject-ов для трех тестируемых в примере сайтов также будут обособленными. Можно сказать, что библиотека шагов Кореи нужна только для того, чтобы эти шаги инстанцировали PageObject-ы именно из корейской библиотеки PageObject-ов.
И хотя приведенный пример шага является очень простым, можно заметить, что он предполагает возможность создания сложных шагов с множеством действий (если программист считает нужным их использование).
Что можно получить от Thucydides multisite tester?
2. Немаловажный момент — это хранение данных. Учитывая количество сайтов и потенциал увеличения этого количества, процесс заполнения XML файлов окажется сильно затянутым во времени и, что еще хуже, дублирование данных будет достигать 60% для некоторых сайтов.
В итоге, ничего другого, кроме как организовать все данные, которые сохраняются в одном файле с иерархической структурой от Apache, придумать сложно. Преимущество заключается в том, что при таком типе структуры мы избегаем излишнего дублирования продуктов, кредитных карт и тому подобных данных для сайтов, где они совпадают. За счет использования принципов наследования есть возможность вынести все общие данные наверх, оставляя для каждого сайта только уникальные параметры.
3. Open Commerce API (OCAPI) — это REST API для сайтов на платформе Demandware, которое позволяет клиентам, партнерам и разработчикам получать информацию о продукте для простой интеграции. Shop APIs обеспечивает простую интеграцию с данными магазина для использования другими системами, приложениями и так далее, с конечной целью — совершением транзакции.
OCAPI позволяет работать со следующими ресурсами:
Перед использованием OCAPI следует настроить в бизнес-менеджере Demandware. Настройки сохраняются в JSON-формате и являются уникальными для каждого сайта. Специально для тест-разработки целей была написана Java библиотека OCAPI, которая автоматически внедряет ocapi.js в клиентскую страничку в браузере и обрабатывает все ее вызовы. Вся коммуникация между OCAPI java библиотекой и ocapi.js проходит через WebDriver JavascriptExecutor интерфейс. Это значит, что прежде чем осуществить запрос к ocapi, надо открыть страницу магазина. Интеграция OCAPI модуля в Thucydides существенно ускоряет выполнение тестов и, тем самым, получение результатов после билда.
4. Гибкая настройка джоб на Jenkins и поднятие Selenium Grid-сервера позволяет запускать тесты в многопоточном режиме, ускоряя процедуру регрессионного тестирования.
Результаты:
После внедрения вышеописанного функционала нам удалось обеспечить своевременное обнаружение и устранение проблем, что в свою очередь позволило внедрить процесс оптимизации и тестирования воронки продаж, на основании которого Crocs добились значительных финансовых успехов. В результате, после проведения необходимого тестирования и оптимизации, в одном только 2012 году маркетинговая команда Crocs добилась $100-миллионных продаж в своих онлайн-магазинах, с последующим ежегодным 19-процентным приростом. Говоря об этом, мы, конечно, помним, что успех e-commerce-сайта заключается не только в качественном тестировании — естественно, это деликатная комбинация качественных дизайнерских решений, хорошего маркетинга, технических ноу-хау и, самое главное, востребованных продуктов, без которых столь высокие продажи были бы труднодостижимы.
Авторы:
Константин Тищенко
Виталий Тарадайко
Александр Мамалыга
Организация фреймворка для автоматизированного регрессионного тестирования:
Для реализации проекта был использован фреймворк Thucydides+JUnit. Именно это решение позволило нам обеспечить максимальную гибкость настройки, внедрить множество вспомогательных методов для работы с веб-элементами, предусмотреть логирование и обработку исключительных ситуаций, и, конечно же, создание красивых отчетов.
На данном этапе мы смогли покрыть следующие локализации сайтов Crocs: США, Канада, Европа, Австралия, Тайвань, Великобритания, Польша, Германия, Корея, Япония, Сингапур, Гонконг, Финляндия, Франция и Голландия.
Так как функционал отдельных локализаций сайтов отличается, количество тестов для сайтов также варьируется. В рассматриваемом случае, минимальное количество составило 70, максимальное — 101 тест.
Среда непрерывной интеграции: Jenkins.
Ключевые технологии, которые используются во фреймворке:
1. Как правило, end-to-end автоматизированное тестирование пишется отдельно для одного конкретного сайта. Множественные отличия в дизайне и логике работы сайта требуют большого количества развилок в коде, что приводит к плохой его читаемости. Поэтому для реализации идеи “один тест должен выполняться на разных локалях и версиях сайта” мы решили использовать рефлексию (механизм исследования данных о программе во время её выполнения, осуществляется с помощью Java Reflection API, который состоит из классов пакетов java.lang и java.lang.reflect). Вот как это можно сделать:
Предположим, что есть некий тест. На высоком уровне тест представляет собой последовательность шагов, которые должны быть выполнены вне зависимости от тестируемого сайта. Сайт определит особенности реализации каждого шага.
@Test
public void testSomething() { // параметры HashMap<String, String> x = new HashMap<String, String>(); HashMap<String, String> y = new HashMap<String, String>(); x.put("x1", "12"); x.put("x2", "13"); y.put("y1", "22"); y.put("y2", "23"); // шаги теста all.goHome(); all.goToSite(); all.login("j.smith", "123456q"); all.complexStep(x, 123, "aaa", "bbb", y); }
Сайт, на котором он будет выполняться, зависит от значения системного свойства site. Это свойство в Java передается как параметр командной строки. Для обращения к нему используется строка кода System.getProperty(“site”); Таким образом, необходимо реализовать коммутацию шагов goHome, goToSite, login и complexStep в зависимости от сайта. Пусть все шаги – это методы класса AllSteps.
@Steps public AllSteps all;
Этот класс – библиотека шагов Thucydides. Прежде всего, он содержит ссылки на конкретные реализации шагов. Предположим, что конкретных сайтов три: Корея, Тайвань и США. Тогда
public class AllSteps extends ScenarioSteps {
@Steps
public KoreaSteps korea;@Steps
public TaiwanSteps taiwan;@Steps
public USSteps us;}
Сердце класса AllSteps — это метод, который называется reflector (отражатель). Его задача – перенаправление выполнения шага от общей библиотеки шагов к одной из трех специфических. В обычной ситуации метод через точку вызывается у объекта. Однако в Java реализован и другой способ сделать то же самое, то есть вызвать объект через точку у метода. Данное действие реализовано в java.lang.reflect.Method. Имя вызываемого метода превращается в строку, что позволяет производить с этим именем разные манипуляции. Предположим, что метод, реализующий шаг для некого сайта, называется так же, как и сам шаг, но с добавлением названия сайта в конец имени метода. То есть login превратится в loginUS, goHome в goHomeUS и так далее.
Для случая с тремя сайтами отражатель может быть реализован так:
@Step
public void reflector(String methodName, Class[] types, Object... args) { if (System.getProperty("site").equals("US")) { try { Method m = us.getClass().getMethod( methodName + "US", types ); m.invoke(us, args); } catch (Exception e) { error("US reflection error " + e.getMessage()); } } if (System.getProperty("site").equals("Taiwan")) { try { Method m = taiwan.getClass().getMethod( methodName + "Taiwan", types ); m.invoke(taiwan, args); } catch (Exception e) { error("Taiwan reflection error " + e.getMessage()); } } }
Главная особенность (и неудобство) заключается в том, что для подобного «перевернутого» вызова метода необходимо создать специальную структуру данных, которая будет соответствовать сигнатуре метода. В приведенном примере это передаваемая снаружи переменная types, которая формируется методом-оберткой, вызываемым из теста, расположенного на самом верхнем уровне.
@Step
public void login(String login, String password) { // method signature Class[] types = new Class[2]; types[0] = String.class; types[1] = String.class; reflector("login", types, login, password); }
Параметр types будет всегда, даже если сигнатура вызываемого метода пуста (в этом случае ему нужно присвоить null или new Class[0]). Значения параметров шага передаются внутрь отражателя без изменений. Так как параметров может быть переменное количество, рефлектор также объявлен как метод, принимающий переменное количество параметров Object.
Все шаги обертки, реализующие обращение к отражателю, должны быть помечены аннотацией
@Step
, что позволит им отображаться в отчете Thucydides. В приведенном примере рефлектор также помечен аннотацией @Step
. Это приведет к тому, что в отчете отражение тоже будет отображаться как тестовый шаг, а сами шаги станут вложенными. Чтобы этого не происходило, аннотацию @Step
можно убрать.Для вывода в лог ошибок отражателя используется метод error, который также является шагом Thucydides. Он ничего не делает, но его параметры (сообщение об ошибке) будут попадать в отчет.
@Step
public void error(String message) {}
Рефлектор переадресует вызов в библиотеку шагов соответствующего сайта. Типичный шаг занимается тем, что переадресует вызов на PageObject, который будет работать со страницей через веб-драйвер. Библиотеки PageObject-ов для трех тестируемых в примере сайтов также будут обособленными. Можно сказать, что библиотека шагов Кореи нужна только для того, чтобы эти шаги инстанцировали PageObject-ы именно из корейской библиотеки PageObject-ов.
@Step
public void loginKorea(String login, String password) { KoreaStorefrontPage sp = getPages().get(KoreaStorefrontPage.class); sp.clickOnLoginLink(); }
И хотя приведенный пример шага является очень простым, можно заметить, что он предполагает возможность создания сложных шагов с множеством действий (если программист считает нужным их использование).
Что можно получить от Thucydides multisite tester?
- Мультисайтовый тестер позволит иметь независимые библиотеки шагов для каждого из тестируемых сайтов. Каждую из этих библиотек может разрабатывать отдельный программист, минимально пересекаясь (и конфликтуя) с автором соседней библиотеки для другого сайта.
- Мультисайтовый тестер позволит иметь независимые библиотеки PageObject-ов для каждого из тестируемых сайтов. Каждую из этих библиотек может разрабатывать отдельный программист, минимально пересекаясь (и конфликтуя) с автором соседней библиотеки для другого сайта.
- Помимо специфического для каждого сайта кода, тестер будет иметь и код, относящийся ко всем тестируемым сайтам. Так как несколько тестеров физически находятся внутри одной программы, то снимается вопрос совместного использования общих библиотек и наработок.
- Кроме технических выгод, есть еще и психологические: иногда заказчик хочет, чтобы сайтов тестировалось несколько, а программа была одна, как в нашем случае.
2. Немаловажный момент — это хранение данных. Учитывая количество сайтов и потенциал увеличения этого количества, процесс заполнения XML файлов окажется сильно затянутым во времени и, что еще хуже, дублирование данных будет достигать 60% для некоторых сайтов.
В итоге, ничего другого, кроме как организовать все данные, которые сохраняются в одном файле с иерархической структурой от Apache, придумать сложно. Преимущество заключается в том, что при таком типе структуры мы избегаем излишнего дублирования продуктов, кредитных карт и тому подобных данных для сайтов, где они совпадают. За счет использования принципов наследования есть возможность вынести все общие данные наверх, оставляя для каждого сайта только уникальные параметры.
- Секции — файл разбит на секции, каждая секция начинается с определенной декларации. Синтаксис декларации начинается с символа “[” и заканчивается соответственно “]”. В такую секцию попадает названия сайта.
- Параметры — имеют типичный формат: ключ=величина
- Комментарии — два варианта: строки которые начинаются с “#” или “;”
3. Open Commerce API (OCAPI) — это REST API для сайтов на платформе Demandware, которое позволяет клиентам, партнерам и разработчикам получать информацию о продукте для простой интеграции. Shop APIs обеспечивает простую интеграцию с данными магазина для использования другими системами, приложениями и так далее, с конечной целью — совершением транзакции.
OCAPI позволяет работать со следующими ресурсами:
- Account
- Basket
- Category
- Content
- ContentSearch
- Folder
- Product
- ProductSearch
- Promotion
- Site
- Store (13.4+)
- Customer (Data API, 14.2+)
- CustomerSearch (Data API, 14.2+)
Перед использованием OCAPI следует настроить в бизнес-менеджере Demandware. Настройки сохраняются в JSON-формате и являются уникальными для каждого сайта. Специально для тест-разработки целей была написана Java библиотека OCAPI, которая автоматически внедряет ocapi.js в клиентскую страничку в браузере и обрабатывает все ее вызовы. Вся коммуникация между OCAPI java библиотекой и ocapi.js проходит через WebDriver JavascriptExecutor интерфейс. Это значит, что прежде чем осуществить запрос к ocapi, надо открыть страницу магазина. Интеграция OCAPI модуля в Thucydides существенно ускоряет выполнение тестов и, тем самым, получение результатов после билда.
4. Гибкая настройка джоб на Jenkins и поднятие Selenium Grid-сервера позволяет запускать тесты в многопоточном режиме, ускоряя процедуру регрессионного тестирования.
Результаты:
После внедрения вышеописанного функционала нам удалось обеспечить своевременное обнаружение и устранение проблем, что в свою очередь позволило внедрить процесс оптимизации и тестирования воронки продаж, на основании которого Crocs добились значительных финансовых успехов. В результате, после проведения необходимого тестирования и оптимизации, в одном только 2012 году маркетинговая команда Crocs добилась $100-миллионных продаж в своих онлайн-магазинах, с последующим ежегодным 19-процентным приростом. Говоря об этом, мы, конечно, помним, что успех e-commerce-сайта заключается не только в качественном тестировании — естественно, это деликатная комбинация качественных дизайнерских решений, хорошего маркетинга, технических ноу-хау и, самое главное, востребованных продуктов, без которых столь высокие продажи были бы труднодостижимы.
Авторы:
Константин Тищенко
Виталий Тарадайко
Александр Мамалыга