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(); } }
/// <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/
Добавить комментарий