Тестирование проекта Ext.Js на Selenium

от автора

Есть три вещи, которые можно делать бесконечно:
1. Наблюдать, как горит огонь
2. Наблюдать, как бежит вода
3. И наблюдать, как кто-то работает

В нашем случае, наблюдать за тем, как крутятся наши over 9000 тестов. Особенно красиво смотрятся Selenium тесты. Выглядит, как будто бешеный тушканчик с вечным двигателем внутри сел тестировать систему.

Не знаю как вас, но меня это затягивает:

Остаток статьи я расскажу маленькую success-story о том, как мы организовали наше тестирование на Selenium

Это был наш второй подход к снаряду. В первый раз, еще в 2009 году, сыпалось все:
• Web драйвер работал только с IE.
• Иногда подвисал при открытии браузера
• Иногда не мог закрыть браузер
• И самое главное: встроенная запись тестов через расширение браузера (т.н. «накликивание») делала плохие тесты для Ext.Js оболочки.

Наш проект написан с использованием Ext.Net – оболочки Ext.Js для .NET. А Ext.Js был явно слишком сложен для Selenium. Он генерировал для клиента случайные идентификаторы элементов, и дополнительно случайные идентификаторы объектов так-же генерировали мы сами (что необходимо для параллелизма и высоких нагрузок нашей платформы).

Вроде бы тест можно легко накликать мышкой, но время жизни этого теста составляло – считанные дни. Хуже того, когда падал тест, то понять причину падения часто было сложней, чем просто перенакликать его заново (что и делали пару раз).

А еще тесты были медленные! Представьте сценарий:
1. Мы вбиваем строку поиска в таблицу с данными
2. Сервер возвращает ответ – пустой грид (что иногда может быть правильным)
3. И вдогонку шлет сообщение об ошибке, если она произошла.

Ext.Js с его асинхронными запросами на любой клик устроен так, что для многих вызовов нам нужно ждать сообщение об ошибке секунд 10 и если мы его не получали, то считали, что поиск отработал нормально.
10 секунд здесь, 10 там и в итоге сам тест на 90% времени состоит из ожидания.

И это еще не все. А вы пробовали разобраться в причинах падения накликанного теста из Selenium лога? Вот так, чтобы повторно не запускать тест, а окинуть взглядом лог и понять суть проблемы. У меня – не получалось.

Через пару месяцев стало понятно, что такое тестирования отнимает у нас больше времени чем приносит пользы и Selenium тесты мы убили.

Проект тем временем все рос, количество функций измерялось тысячами и вот, год назад героический selmaril вернулся к этой проблеме с обновленным Selenium и обновленным пониманием, как тестировать Ext.Js

Его решение было: писать API.

Мы решили отказаться от записи тестов мышкой, но прийти к возможности записывать NUnit(!) тесты с короткой нотацией в виде:
1. Отфильтровать пользователей по дате создания = сегодня.
2. Отсортировать по имени
3. Открыть первую запись
4. Изменить пароль.
5. Сохранить.
6. Нажать ОК, если выскочит окно «Вы уверены».

Для этого было нужно API над Selenium и NUnit, которое само состояло бы из очень простых элементов, но позволяющих оперировать не мышкой и DOM моделью, а с объектами интерфейса Ext.Js.

Месяц напряженной работы, и первая версия API появилась.

Вот образец теста:

        /// <summary>         /// DocumentOperationGridOperationTest         /// </summary>         [Test]         public void DocumentOperationGridOperationTest()         {             var baseDocName = typeof(BaseInDocument).ModelName();             var implDocName = typeof(BaseInDocumentImpl).ModelName();              using (var grid = Env.Navigation.OpenList(implDocName))             {                 var operationCaption = "Документик_Создайся";                 grid.Toolbar.Click(operationCaption);                  var docForm = Env.TabPanel.GetForm(implDocName);                  docForm.Toolbar.Click("Создание", "Завершить операцию {0}".FormatWith(operationCaption));                  docForm.Close();             }              using (var grid = Env.Navigation.OpenList(baseDocName))             {                 var operationCaption = "Редактируем_Наследник";                 grid.Data.First().Select();                                 grid.Toolbar.Click("Операции", operationCaption);                 grid.Toolbar.Click("Открыть");                                 var docForm = Env.TabPanel.GetForm(implDocName);                 var fieldValue = docForm.GetField<TextField>("SomeNewField");                 Assert.False(string.IsNullOrEmpty(fieldValue.GetValue()), "Не заполнилось поле которое должно быть заполнено в классе операции, то есть не вызвался класс операций для документа");                 grid.DeleteFirstRow();                 docForm.Close();             }         } 

А вот как выглядит реализация API

        /// <summary>         /// Получить элемент из контекстного меню         /// </summary>         /// <param name="fieldName">Системное имя поля сущности, в ячейке которой у данной записи надо вызвать контекстное меню</param>         /// <param name="buttonCaption">Путь к элементу контекстного меню, по которому надо получить элемент (кнопку действия например)</param>         /// <returns>Найденный элемент в контекстном меню</returns>         public IToolbarElement GetContextMenuItem(string fieldName, string[] buttonCaption)         {             var buttonsFullPath = Oreodor.Utils.EnumerableExtensions.ToString(buttonCaption, "->");             Env.AddHistory("Получить элемент в контекстном меню ячейки для записи с Id - SysName = {0} - {1}, для колонки {2}, по пути '{3}'".FormatWith(                                                                                                                                                          Id,                                                                                                                                                          this.Data.ContainsKey("SysName") ? this.Data["SysName"] : string.Empty,                                                                                                                                                          fieldName,                                                                                                                                                          buttonsFullPath));              IToolbarElement menuItem = null;              if (buttonCaption.Length > 0)             {                 var menuItems = new List<IToolbarElement>();                 if (IsContextMenuVisible(fieldName))                 {                     menuItems.AddRange(GetCurrentContextMenu());                 }                 else                 {                     menuItems.AddRange(ShowContextMenu(fieldName));                 }                  menuItem = Toolbar.CheckItem(menuItems, buttonCaption[0]);                  if (buttonCaption.Length > 1)                 {                     for (var i = 1; i < buttonCaption.Length; i++)                     {                         var menuCaption = buttonCaption[i - 1];                         var itemCaption = buttonCaption[i];                          var menu = menuItem.Menu.ToList();                          Assert.That(menu.Count() != 0, "Меню '{0}' не должно быть пустым, т.к. в нем ещё надо найти '{1}'.".FormatWith(menuCaption, itemCaption));                          var candidateToolbarItem = Toolbar.CheckItem(menu, itemCaption);                          Assert.That(candidateToolbarItem != null, "Проверка наличия элемента '{0}' в меню '{1}'. Найдены следующие элементы: {2}".FormatWith(itemCaption, menuCaption, menu.Aggregate(", ", (aggregated, item) => aggregated + "'" + item.Text + "'")));                          menuItem = candidateToolbarItem;                     }                 }             }              return menuItem;         } 

Также была решена важная проблема – быстрое понимание ошибки из лога TeamCity. Api генерирует вот такой отчет по каждому тесту.

Итог: сейчас мы покрыли около 80% поведения интерфейса системы. Делюсь выжимкой нашего опыта, для тестирования Ext.Js на Selenium Вам нужно писать свою обертку для тестирования для решения следующих проблем:
1. Лаконичность и понятность теста
2. Устойчивость теста к изменением системы
3. Скорость работы тестов
4. Легко разобраться в причинах поломки теста
Тесты, созданные накликиванием из интерфейса браузера скорее всего Вам не подойдут.

P.S.
Если у вас Ext.Js проект и вы решите покрыть его Selenium тестами — обращайтесь за советами, постараемся помочь.

ссылка на оригинал статьи http://habrahabr.ru/post/181660/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *