Знакомство с фронтенд-тестированием. Часть третья. E2E-тестирование


Оглавление (нажмите, чтобы открыть):

Знакомство с фронтенд-тестированием. Часть третья. E2E-тестирование

Фронтенд-тестирование, один из способов написания e2e-тестов.

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

Результат всего этого — готовый к использованию инструмент для e2e-тестирования во фронтенде, а именно инструмент позволяющий протестировать всё приложение целиком, причём мы сделаем это с точки зрения пользователя, по сути, автоматизируя все его действия.

Стоит учитывать что полагаться только на e2e-тесты не стоит, нужны так же хорошо написанные интеграционные и юнит-тесты, e2e тесты по сути проверяют что все «участки» приложения связаны и функционируют корректно. Еще стоит отметить что e2e-тесты довольно медленные (возможно ускорить их прохождение если использовать «селениум грид», для того что б они выполнялись параллельно на разных «енвайрментах») и ихнее поведение может быть не предсказуемым. Они могут то проходить, то падать — причем без видимых изменений, исключительно из-за I/O, к сожалению эту нестабильность полностью убрать нельзя, можно только минимизировать её. Из этого стоит сделать вывод, что нужно писать один E2E-тест на двадцать других, и лишь тогда, когда они действительно необходимы.

Теперь что касается написания тестов, для этого нам нужен будет сервер с самим приложением которое мы тестируем, это будет приложение о котором я писал в прошлом посте, которое будет запускаться по адресу https://localhost:3000/ и https://localhost:3000/api, его мы и будем тестировать.

Теперь относительно самого инструмента тестирования. Разберем какие нам нужны библиотеки и что мы устанавливаем в package.json.

Так как весь код мы будем писать код в es2015 с использованием async / await и прочих возможностей, то его мы будем «транспайлить» через Babel с нужными нам «пресетами», для этого нам понадобиться:

Так как мы будем проверять наш код нам нужен будет eslint, так же будет использовать prettier для форматирования кода. Пакет isomorphic-fetch понадобиться для отправки запросов на сервер. Для тестирования и генерации «репорта» мы будем использовать mocha и mochawesome, а пакет fs-extra нужен будет для удобного сохранения скриншота в «репорте» mochawesome. Для того чтобы запустить браузер и автоматизировать наши действия будем иcпользовать популярный selenium-webdriver и chromedriver.

Для самого запуска у нас будет использоваться две команды `npm run one` для запуска одного теста, к примеру `npm run one src/e2e-tests/home-page/index.js` и `npm run all` для запуска всех тестов.

Для написания тестов мы будем использовать mocha. Суть запуска теста сводиться к тому что мы запускаем mocha с файлом настроек mocha.opts:

Что важно из этого файла: ` — reporter mochawesome` так мы устанавливаем через что мы будем генерировать «репорты» от e2e-тестов, ` — require babel-core/register` и ` — require babel-polyfill` нужны для того, чтобы писать код в es2015 c нужными «плюшками», а ` — require isomorphic-fetch` нужен что б мы могли делать запросы на сервер с помощью функции `fetch`, а в нашем нашем устанавливать или очищать нужные данные перед началом каждого e2e-теста, ну и последние `src/configuration/setup.js` мы указываем файл настроек, где мы будем в функции before «устанавливать» драйвер, а в after — очищать его. Эти функции запускаются до и после выполнения всех тестов и настраивают окружение, которое могут использовать тестовые функции. Настройка драйвера происходит асинхронно и мы используем async/await, чтобы сделать код красивее. Сама настройка находить в файле setup.js, дальше мы разберем его подробнее.

Структура выглядит следующим образом:

Итак в файле configuration/setup.js. Как уже говорилось ранее в функции before мы «готовим» драйвер, а в after — очищаем его. Подготовка драйвера будет запускать браузер, а очистка — закрывать его.

Первые две строки подключают webdriver — драйвер для браузера. Принцип работы Selenium Webdriver заключается в наличии API чтобы управлять различными браузерами, в данном случае это происходит через через драйвер браузера). Драйвер, который мы используем — chromedriver. Далее мы настраиваем веб-драйвер и устанавливаем его в глобальную область видимости через `global.driver` что б в дальнейшем везде обращаться к нему через `driver`. Так же в этом файле мы делаем две важные вещи. Первая, мы генерируем скриншоты во время ошибки, чтобы было проще разобраться почему «упал» тест:

Тут мы после каждого теста проверяем его состояние и если он завершился с ошибкой, то мы создаем скриншот, с названием теста и с помощью addContext добавляем его к «репорту» для данного теста.

Вторая, не маловажная вещь — это «пропуск» последующих тестов после неудачного теста. Что это значит и зачем это нужно? Представим себе такой тест:

В данном случае, если не выполнился первый шаг, с условием `counter value on Home Page should be 0`, нам нем смысла выполнять последующие шаги в этом тесте и внутренние группы тестов, так как они базируется на первом шаге, их нужно просто «пропустить» чтобы не тратить время на ненужные шаги и сразу перейти к следующему тесту. «Пропуск» тестов в configuration/setup.js именно это и делает.

В конце каждого теста, мы проверяем состояние теста, и если он завершился с ошибкой, мы записываем в переменную `FAILED_TESTS_BY_PARENT` — путь из названий до текущего теста, не включая название самого теста, что означает, что мы записываем путь из названии до родителя, далее в начале каждого теста, мы проверяем если какой либо путь из названии массива `FAILED_TESTS_BY_PARENT` входит путь из названий в текущем тесте, то мы его просто пропускаем. Собственно это и есть вся настройка инструментария.

Перейдем к написанию самих тестов. Каждый e2e-тест храниться в папке src/e2e-tests. Точка входа в каждый файл это index.js. Сейчас в папе находить только один тест home-page, поэтому его и разберем.

В данном случае мы хотим протестировать работоспособность страницы Home. Для лучшего понимая, она выглядит так:

Разобьем тесть на две части Clicks которая проверит работоспособность счетчика кликов и Links которая проверит что ссылка Home активна когда мы находимся на главной странице. Так как эти нужны только для демонстрации, то они очень простые, в реальности же, тесты могут быть намного сложнее.

Стоит заметить что все «хелперы» разбиты на группы, а именно: api — для работы напрямую с сервером или установки начальных значении в базе данных; page — для удобства перехода по страницам и dom — для работы с самими элементами: нажатия, ввод, перемещение курсора и прочее.

В данном тесте в начале мы устанавливаем значение счетчика нажатий в 0 строчкой ` await api.set(“settingsHomePage”, < clicksCount: 0 >);` и дальше переходим на страницу Home `await page.home();`.

Сам тест Clicks выглядит следующим образом:

Разберем подробнее что в нем происходит. Мы проверяем что счетчик нажатий равен 0, далее мы пытаем нажать на страницу и «посмотреть» увеличиться ли счетчик на единицу или нет и так 4 раза. Во втором шаге мы «закомментируем» шаг нажатия, чтобы показать как работает «пропуск» тестов, тем самым мы попытаемся сымитировать ошибку и посмотреть как будет выглядеть отчет. Стоит так же заметить что все привязки к элементам страницы выполняются через специальные классы с префиксом `e2e-` — это хорошее требование которое позволяет на «завязываться» на классы которые могут поменяться в процессе разработки и иметь свои отдельные классы для тестирования.

После запуска тестов можно будет увидеть отчет в консоли:

Разбор тестового задания frontend разработчика

Привет, участникам и гостям. Ниже текстовая версия разбора третьего тестового задания.

Так же есть видео версия разбора:

Если ошибка / хороший ход повторяются — в общем ревью это не указывается во второй, третий и так далее разы. Для некоторых моментов, я все же повторяюсь, это сделано либо по невнимательности, либо мне хочется заострить внимание на отличном решении / проблеме.

Общие проблемы

  • Многие ошибки видны, если просто запустить демо, не открывая код. Невнимательность или «пофигизм» — навык джуниора. Чем чище ваще демо, тем лучше впечатление о вас.
  • Ни одного теста. Стыд! Для кого я пишу такие подробные туториалы? (Раз, два)
  • Приложение падает из-за отсутствия try/catch / ErrorBoundaries . Чаще всего, это было связано с ошибкой загрузки библиотеки для google sign in.
  • Нет разделения на редьюсер одной новости и всех новостей
  • Верстка не адаптивная (не учитывал, так как оформление не являлось обязательным условием)

Общие плюсы

  • Работа с api вынесена отдельный модуль
  • Присутствуют хелперы (общие функции), вынесенные отдельно
  • Оформление
  • Желание сделать больше чем было в ТЗ, либо использовать новые техники и подходы к архитектуре

Частые вопросы:

Для сравнения авторства, если автор новости заходил через google:

  • авторизоваться с помощью google sign in и получить google токен
  • отправить токен на /api/v1/auth/google
  • получить токен нашего приложения и декодировать его (для получения данных о пользователе)
  • сравнить id (article.creator._id и id декодированный из токена гугла)

Regies Linkas

Собственные замечания в readme, не разбирал google jwt

Минусы:

У новости троеточие, даже если она короткая.

В просмотре новости, текст так же обрезается.

Сессия не сохраняется между рефрешами.

Можно не биндить в конструкторе, при использовании стрелочных функциий

NewsItem грязноват, например: один раз проверено this.props.detail и потом еще доп.проверки, возможно недорефакторено

Опять лишняя проверка, если в cdm сделана проверка на новость, то нет смысла в шаблоне еще раз проверять это

Для методов delete & edit можно было бы заранее настроить axios заголовки, или вынести работу с API в отдельный модуль. (будет показано в следующей работе у Виталия Володенко)

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

Плюсы:

Передача флага editable в тупой компонент

Есть обработка ошибок, но нет уведомлений (о чем в readme признались)

Vitaly Volovodenko

Не сторонник «веселых» исполнений, но в данной работе стиль выдержан полностью, а не частично, поэтому отнесусь к этому нейтрально с юморком.

Минусы

Плюсы

Бэк подпилен под свои нужды — можно было бы указать, под какиие.

API для запросов + пример, когда запрос под api не подходит

Нейтрально

Можно было бы вынести только state.user и не углубляться ниже.

Так же, работа сильная, можно добавить использование селекторов.

С реакт работаю 6 месяцев, с JS — 10 месяцев, с TS — 2 месяца.
Выполнил бы еще и бонусное задание, но уже устроился на работу, поэтому будет некогда.

Rostislav Futornoy

.env файл не в .gitignore

Иконки не очень аккуратно сверстаны.

Минусы:

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

В reducers не используются константы, хотя они описаны.

Вместо того, чтобы добавлять новый флаг для условия в render, можно было взять курс на reducer новостей, и проверять флаг оттуда, но так как в reducer не хватает isFetching флага и ошибки — то решение со state допускается, хотя я бы лучше «прокачал» reducer.

NewsContainer подписан на user, но user не используется. Так же это потенциальная проблема для производительности, так как при изменении в user будет перериисовка и в NewsContainer.

Рекомендую использовать более подробную запись для массива с объектами. Подробнее на StackOverflow + документация

Нельзя сравнивать авторство по не уникальному атрибуту (в данном случае, по имени).

Плюсы:

Ошибки показываются клиенту

Сергей ZackFox

Возможно, когда работа была послана, этого предупреждения еще не было, но тем не менее, не забывайте проверять свои зависимости, с помощью npm audit

Минусы:

Нет иконок в карточке новости, на странице всех новостей.

Избыточное условие в проверке, компонент не рисуется без news.

Плюсы:

Создание новости с графическим редактором

Аккуратно в API описан signIn + использованы cookies. Наконец-то, сессия не сбрасывается!

Helper getDecodedUser — как раз, показано как можно вытащить id (отсылка к предыдущей работе), по которому потом сверять(?).

Обыденные hoc‘и, но хочу отметить их.

Герман Топалов

Readme ок! Забыл лишь про env файл.

Минусы

В письме было указано, что времени на полировку не хватило, но ревью от этого «мягче» не стало, так как работа сильная.

У формы входа отсутствует валидация. Например, не «отключаются» (disabled атрибут) кнопки, если поля пустые. Хотя у добавления новости — валидация есть.

Нет возможности вернуться на главную страницу с формы входа

Если пользователь что-то ввел в новости, и жмет «отмена» — не спрашивается подтверждение.

Плюсы

Используется годная библиотека для уведомлений react-tostify

Подтверждение удаления новости присутствует.

Используется отдельный layout для некоторых роутов

Использование title для иконок (внимание к деталям!)

Использование extraArgument в redux-thunk, затем переменную api можно вызывать так:

(внимание на третий аргумент, документация)

Решение проблемы с инициализацией google api (библиотека будет точно загружена к моменту выполнения логина).

Любопытно

Михаил Карпов

Есть демо и скриншот в readme.

Новости появляются с анимацией, есть анимация на иконках.

Чтобы запустить проект, нужно не только создать .env файл с ключом, но еще и заменить в config API_URL

Кнопка добавить новость — не очевидная.


Минусы

Редьюсер новостей и новости совмещен. Это две разные сущности, поэтому лучше их разбить на свои отдельные редьюсеры.

Плюсы

Решение с middleware по проверке токена + проверка токена в момент загрузки приложениия

Наличие ui-компонентов. Но я бы добил эту затею, например, post_button состоит из компонента кнопка/ссылка, определенного цвета и текста. Либо вместо одного компонента «кнопка+ссылка», сделать два «кнопка» и «ссылка».

Любопытно

Хак(?) для редиректа после удаления новости

Нейтрально

Флажок статуса загрузки (loading) сделан непривычным для меня способом.

Александр Блажко

В решении используются immutable и саги. Подход — ducks.

Добавлены некоторые «плюшки», например — поиск новости по заголовку.

Минусы

Мертвый код в комментариях

Скорее минус, чем нейтрально: проверка истечения токена (так как приходится дублировать. На крайний случай, можно было бы вынести в хелпер повторяющуюся часть). Сократить количество кода в компонентах и не только, поможет иное решение, например проверка в middleware, как это сделано у Михаила.

Много «буков», если так можно выразиться. Например, для получения новости. Проблема уже известная — снова все новости и конкретная новость объединены в один редьюсер. Нужно разделить. Затем простой запрос за новостью в cdm и подписка на редьюсер новости в mapStateToProps.

Повторяющийся код (валидация, вытаскивание переменных и тд тп) — лучше выносить в хелперы. Например, повторение в строках 91-93 и 173-175 в ducks/auth.js

Так же про повторение и для селекторов. Можно было бы добавить это в отдельный файлик и не дублировать код. Но за использование селектора — плюсик.

Плюсы

Общий шаблон для создания/редактирования новости. Стоит отметить, что с ростом разницы между этими формами, может расти сложность шаблона и в какой-то момент, может быть выгоднее сделать 2 разных компонента.

Любопытно

Используется Record вместо привычного мне Map, может быть автор прояснит плюсы?

Итого: считаю, что после данного обзора у автора будут мысли для рефакторинга и работа станет меньше по объему символов и легче для чтения.

Ali Gasymov

Минусы

Адрес API не вынесен в константу

Опасные имена для сохранения в LS, которые с легкостью может перезаписать другое приложение. Рекомендую использовать уникальный префикс.

Плюсы

в итоге удаление превращается в легкий delete по ключу

Json.stringify для более безопасного сохранения новости

Любопытно

Решение проблемы с «еще не загруженным» api для google sign in

Андрей Голушкин

Минусы

Warning при сборке

Warning: postcss-cssnext found a duplicate plugin (‘autoprefixer’) in your postcss plugins. This might be inefficient. You should remove ‘autoprefixer’ from your postcss plugin list since it’s already included by postcss-cssnext.

Кнопка «Войти» меняется на «Выйти», даже если ошибка получения токена (причем, уведомление с ошибкой показывается ОК)

В подробностях открывается не та новость. Вероятно из-за опечатки. С нормальным сравнением, работает.

cdm расположен ниже метода render. Это не ошибка, но большинство проектов пишутся опираясь на eslint-правило в котором указан порядок следования методов.

Много кода для перехода на редактирование новости, начинается через state. Нужно использовать Link из react-router

Плохое условие для показа кнопок редактирования/удаления

Токен вытащен напрямую, по документации нужно использовать:

Плюсы

Компонент кнопки вынесен отдельно

Сергей Козлов

В работе используются саги и Ant design

Минусы

Проверка доступности редактирования/удаления новости сделана в самой новости. Компонент новости должен просто принимать флаг, например: editable — true / false . В данный момент, он знает больше чем положено.

[придираюсь] Иконка глаза не очевидна для редактирования

Плюсы

Работу можно рассматривать как хороший пример для работы с сагами.

Нейтрально

Шаблон новости (короткая, полная версия) в одном файле.

Роман Бабахин

Проект сделан с ��

Минусы

Если параметров у функции много, лучше класть их внутрь объекта, чтобы не «запоминать» порядок.

Нет автоформатирования кода, или автор использует непривычные для меня настройки, например строки 25-40 в NewsContainer.

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

Bind в констукторе — устарело. Можно просто использовать стрелочные функции для методов.

В работе выполняется поиск по заголовку, но для поиска всегда необходимо использовать trim и toLowerCase методы.

Избыточная запись, можно указать LoginPage в вызове connect.

cwm «устарел» (deprecated). Куда вынести эти расчеты рекомендации нет, нужно обсудить «желание» автора и подумать.

переменная expires — const (eslint спасет от таких проблем) (данный файл, вообще, не используется в проекте)

Плюсы

Выполнен дополнительный функционал, дизайн приятный, разбита на множество компонентов… (и в целом работа неплохая), но выше уже были описаны схожие плюсы, поэтому не повторяюсь.

Нейтрально

NewsContainer с вложенными роутами, включая рендер роута через функцию

Вижу намек на cache — это здорово, но я не заметил как кэш сбрасывается(?)

Игорь Лученков

Минусы

Не все зависимости указаны в package.json

Не подчищен console.log

Вновь отмечу, что компонент пытается выяснить, можно ли показывать кнопку «редактирования» + ошибка, что выяснение идет не по уникальному свойству.

React.Fragment и <> на проекте. Хотелось бы в едином стиле.

Вероятно «старый код«, либо неясно назначение isEditing , который всегда true

Опечатка в названии функции (должно быть feedsReducer)

Поскольку ниже в плюсах отмечен Flow, хотелось бы большего покрытия. Как минимум, это обязательно (так как это просто) покрытие propTypes, ниже указана библиотека для этого.

Плюсы

Использование recompose отмечу как плюс. Хотя скорее всего, это привычный для автора способ. С одной стороны выглядит красиво и компоненты очень «тупые», с другой стороны — приходится бегать по файлам, чтобы подсмотреть примитивные обработчики.

Работу предлагаю к рассмотрению как туториал по работе с recompose

Евгений Захаров

В Readme есть подробное описание. Частично цитирую:

Проект создан с помощью структуры react-modern — это моя реализация фрактальной структуры приложения. Это мой первый опыт написания приложений на основе этой структуры, так что удалось найти некоторые проблемы и баги в ней. Само собой следующие приложения, написанные с этой структурой, будут более доработанными и красивыми.

Минусы

О тестах вспомнил только на середине пути, так что не стал их писать.

Евгений, этот мотивирующий минус для тебя лично.

Можно ли обойтись без таймера? Считаю их ненадежными.

Плюсы

подстановка токена (но плохое имя у переменной в LS)

Нейтрально

Зачем во всех названиях call? Чтобы отличить, что это http-вызов?

Любопытно

Еще раз отмечу, подход react-modern. Кому интересно — welcome в работу Евгения.

Итого

  • Виталий и Герман — самые сильные работы
  • Посмотрите работу Сергея если хотите использовать redux-saga
  • Посмотрите работу Игоря, если хотите использовать recompose

Посмотрите остальные работы и проанализируйте плюсы/минусы, указанные в review.

Сквозное тестирование (end-to-end): что, зачем, почему

Тестирование в больших компаниях, в enterprise, чаще всего дело сложное и неблагодарное. Разрыв между бизнес-подразделениями и IT огромный: когда разработчик имеет видение на уровне кода, а проверку – на уровне модульных тестов, а заказчик мыслит работающими или неработающими даже не услугами, а целыми процессами, выходящими за рамки одной команды разработки, а то и целого подразделения\компании. И просит организовать бизнес-тестирование, или сквозное тестирование, или тестирование на основании сценариев от начала и до конца (end 2 end).

Давайте начнём с самого начала – с двух столпов, откуда появилось это пресловутое «сквозное бизнес-тестирование», а именно с пирамиды тестирования и со стандарта ISO9000.

Пирамида тестирования

С пирамидой тестирования, наверняка знаком любой тестировщик, поднаторевший в своей профессии и набившей шишек при общении со смежными подразделениями. Особенно часто к ней приходится апеллировать при обосновании автоматизации тестирования. Какие тесты дешевле и важнее разработать? А запустить?

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

Мастер Йода рекомендует:  9 лучших плагинов WordPress для миграции

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

Если мы добавим и эти условия – посмотрим как наша система взаимодействует со внешними системами – поставщиками и потребителями, с нашим окружением, то есть проведём системное тестирование, то легко увидим, что сложность тестирования тоже возрастёт. Нам нужно будет добиваться одновременной работоспособности всех взаимодействующих систем, хотя и без привлечения специалистов по ним. Нам пока что достаточно просто принять какие-то данные от наших поставщиков и передать наши данные нашим потребителям. В правильной последовательности и формате. Дальнейшая судьба данных нас не волнует. Главное – наша система работает правильно в правильном окружении. И всё бы тут хорошо – для нашего заказчика мы можем провести уже полномасштабную демонстрацию, да только в реальной жизни это ещё не все критерии успеха для нашей разработки. Конечно хорошо, что у заказчика появилась его квартира в прочном доме, но если от неё надо добираться, перелезая через колючую проволоку, затем на каноэ по озеру с крокодилами в шалаши, кишащие змеями, то, возможно, мы что-то не то и не там сделали?

Поэтому тут первая идея для сквозного тестирования – проверять не только наше окружение, но и все взаимосвязанные системы, через которые проходят данные принимаемые или отправляемые нашей системой. А это, в свою очередь, означает, что мы должны будем совместить несколько таких «пирамид тестирования» между собой. Постройка хрупкого моста, по которому мы проведём за ручку данные, ценные для пользователя.

Вот только вопрос как это делать? Кому это делать? Как собирать воедино?

ISO9000

Серия стандартов, описывающих системы менеджмента качества, в том числе говорит о том, что любой процесс в организации должен быть описан, задокументирован, даже если это процесс выдачи граблей по осени дворнику. А раз так, что ни один процесс, который проходит внутри ПО, используемого и разрабатываемого в организации, не может не быть описан. Вопрос в том, как это делать? Конечно, лучшее описание, с точки зрения BDD — это описание поведения тестами, под которыми будет лежать пирамида тестирования. Но мы сразу же вернёмся к нашей дилемме с объединением нескольких пирамид тонкими канатами от верхушки к верхушке, по которым без страховки будет ходить наш канатоходец-заказчик и его пользователи.

Process approach is a management strategy that requires organisations to manage its processes and the interactions between them. Thus you need to consider each major process of the company and their supporting processes.

All processes have:
• inputs;
• outputs;
• operational control;
• appropriate measurement & monitoring.
Each process will have support processes that underpin and enable the process to become realised

Для этой цели лучше всего подходят бизнес-диаграммы, и чаще всего используются стандарты вроде UML, BPMN, ARIS и пр. А сами процессы становятся блок-схемами с нанизанными на них «кубиками». Между «кубиками» происходит взаимодействие, в стандарте BPMN — это поток действий и поток сообщений. И вот это как раз то, что нам нужно!


Любая компания, которая хочет иметь сертификат и следует стандарту ISO9000, скорее всего, обзавелась такими схемами, и они являются неотъемлемой частью верхнеуровневых требований. Если в компании работают хорошие аналитики, то, скорее всего, к низкоуровневым требованиям будут спускаться ссылки-требования на отдельные действия из схем. Они-то нам и нужны.
Фактически, на схемах мы можем увидеть процесс целиком, и понять, какой сценарий нам нужно построить, и к какой системе\команде бежать с какими данными в какой момент.

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

Что на практике

Итак, мы имеем две вводных – у каждой команды\системы должна быть подготовлена пирамида из тестов – от самых мелких, модульных тестов, до сложных системных тестов, а так же тот факт, что в рамках организации у нас обязаны быть описаны требования в виде бизнес-процессов. Этот факт нам позволит быстро ответить заказчику, какой бизнес-процесс как работает, и на каком моменте из-за чего ломается, а самим, при получения дефектов с промышленной эксплуатации быстро произвести root cause analysis (анализ корневых причин возникновения дефектов). В теории.

А на практике всё опять ложится на тестировщика – как из вороха тестов, тем более чужих, выбрать нужные, выстроить их в цепочку, а на вход каждой из систем подать нужные данные и сверить с корректно определённым ожидаемым результатом?

Самый простой вариант – изначально разрабатывать тесты на основании бизнес-моделей, а деление команд делать по проектам, реализующим тот или иной бизнес-процесс. Для этого в некоторых инструментах управления тестированием есть уже возможность загружать BPMN-схемы (например для HPE ALM – поддерживается загрузка в формате XPDL). HP ALM сам разобьет схему на набор требований (действий), а при желании создаст иерархию требований (модуль Requirements->Business Models). Далее наше дело покрыть требования тестами, а далее выстроить требования, а значит и тесты в цепочки, покрывающие наш бизнес-процесс. Эти цепочки в HPE ALM называются «путями» (path), и позволяют увидеть все комбинации последовательностей. При желании требования, цепочки можно сразу сконвертировать в тесты.

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

Сколькими путями может дойти белочка до шишки?

В этом случае нам нужно будет открыть тесты каждой из команд, найти привязанные к фигурирующим в бизнес-модели требованиям, и выстроить из них цепочки, сохранив в «общем пространстве». Создание общего пространства – это какой-то суррогат, но в любом случае оно должно быть, пусть в виде амбарной книги, excel, или проектной области в инструменте управления тестированием. Если снова говорить о HPE ALM, то за данный функционал отвечает модуль BPT (Business Process Testing), заодно позволяющий передавать результаты одного теста в параметры другого. Впрочем, при желании и упорном труде на HPE ALM это возможно и реализовать через перестроения тестовых наборов (Test set) в поток выполнения (Execution flow). Тогда при запуске полного набора будут по очереди вызываться тестировщики, ответственные за прохождения каждой из компонент сквозного сценария.

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

В итоге, можно сделать два вывода:

1) для сквозных сценариев используются с большой долей вероятности уже ранее разработанные тесты для каждой из систем, входящей в цепочку (сценарий) бизнес-процесса

Можно все полные тестовые наборы компании представить в виде разреженной матрицы, где по столбцам распределены тесты для каждой системы (для простоты – системные), а по строкам – бизнес-процессы. То есть для тех или иных бизнес-процессов надо выбрать\создать тесты, покрывающие бизнес-процесс, установить взаимосвязи. Если покрытия нет – это повод восполнить пробелы в тестовой модели, либо удостовериться, что качество обеспечивается другими уровнями тестирования (интеграционное тестирование, модульное тестирование, ревью кода и прогон его через анализаторы).

2) Необходим инструмент наблюдения, трассирования и актуализации бизнес-процесса на предмет синхронизации с тестовой моделью.

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

В заключение

Организация наглядного и правильного тестирования по бизнес-процессам – сложная и очень дорогая вещь. Обратите внимание, что E2E тестирование – это не просто приёмка, пользовательское тестирование, которое будет выполнять заказчик, это выстраивание мостика, с учётом всех возможных ситуаций, по которому пойдёт заказчик и поведёт за собой в ногу пользователей.

Ещё раз – E2E – это не прогулка на Ладе-Калине через мост, и даже не проезд на двух камазах. Это сложная инженерная работа, обвешивание мостов датчиками и проведение всех возможных проверок и ситуаций — по крайней мере описание этих сценариев.

Нужно или нет вашей компании такой идеальный чистовой прогон – дело исключительно ваших целей и потребностей. Всегда, как и при любом тестировании, следует оценить потенциальные риски от пропущенных дефектах на этой стадии, так и стоимость работ по подготовке и проведения сквозного тестирования. Оценить, что из этого обойдётся вам дороже и только потом действовать. Но в случае сквозного тестирования по бизнес-процессам следует помнить, что оно не имеет смысла без прочного фундамента в виде 100% passrate unit-тестов (

90-100% coverage), без интеграционных тестов (

60-80% coverage, 90-100% passrate), без системных тестов (20-40% coverage, 80-100% passrate). Устанавливать критерии успешности (quality gates) – это больше требования к качеству выпускаемого продукта, главное здесь помнить, что объем E2E тестов – лишь верхушка пирамиды (1-2% coverage,

99% passrate), которая не должна быть больше его основания, не быть при этом затычкой дыр с предыдущих этапов. Это – дополнение, которое априори считается закрытым на предыдущих этапах.

Организация подобного тестирования – главным образом работа по подготовке и синхронизации тестовых случаев и данных (тест-аналитика), а так же комплекс организационных мероприятий, синхронизация команд в одном месте в одно время на работоспособном тестовом полигоне. Помня это, не следует пробовать показывать заказчику «сквозное тестирование» раньше срока, чтобы не тратить время сразу большого количества людей без всех работающих компонентов, собранных воедино.

Знакомство с фронтенд-тестированием. Часть третья. E2E-тестирование

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

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

В итоге я решил написать его сам.

Что такое тестирование?

На мой взгляд, тестирование — это код, который проверяет, работает ли код твоего приложения, так называемый «промышленный код», согласно ожиданиям. Некоторые люди подразумевают под этим TDD (разработку через тестирование), специфическую методологию тестирования, где тесты пишутся в самом начале и направляют дальнейшую разработку продукта.

Прим. перев. О том, зачем нужна TDD, читайте в нашей статье.

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

К сожалению, индустрия объединила идею тестирования и TDD, и из-за этого у кода, написанного разработчиком параллельно с промышленным кодом, нет стандартного определения. Я решил назвать его просто тестированием.

Зачем нужно тестирование?

Нет, я не собираюсь обсуждать, зачем нужно тестировать код. Если вы не хотите тестировать, не делайте этого. Для вас будет очень неприятно вручную проверять ваше приложение снова и снова. Те докучливые баги, которые вы запомните, исправляя их, будут возвращаться снова, чтобы лишить вас сна. Запуск кода в продакшн будет сопровождаться риском и страхом.

Нет, я не собираюсь обсуждать, зачем нужно тестирование.

Типы тестирования

Другая область, сбивающая с толку людей, начавших изучать тестирование — это различные типы тестов. Если вы изучали эту тему, вы наверняка слышали о юнит-тестировании, интеграционном тестировании, end-to-end (E2E) тестировании и компонентном тестировании.

И что еще хуже, каждый человек описывает эти понятия по-своему.

Опять-таки, вопрос терминологии меня заботит мало — я считаю, что у типов тестирования нет строгого определения. На мой взгляд, все тесты лежат в диапазоне, который начинается с юнит-тестов и заканчивается E2E-тестами.

Многообразие типов тестирования

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

Прим. перев. Советуем также прочитать наш перевод статьи «Зачем нужны юнит-тесты».

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

Легко тестировать элемент кода отдельно, когда он не зависит от других элементов вообще. Но что, если я хочу протестировать юнит, который зависит от другого элемента кода? Мы можем сделать две вещи: протестировать два юнита вместе или замо́кать один из них.

Если мы тестируем два элемента кода вместе, это может считаться юнит-тестированием? Педант скажет, что нет. Я скажу, что это не имеет значения. Я обычно называю такие тесты юнит-тестами, но вы можете называть их интеграционными тестами или двойными юнит-тестами.

Что же такое «мок»? Давайте посмотрим на примере:

Testing Your Frontend Code: Part III (E2E Testing)

A while ago, a friend of mine, who is just beginning to explore the wonderful world of frontend development, asked me how to start testing her application. Over the phone. I told her that, obviously, I can’t do it over the phone as there is so much to learn about this subject. I promised to send her links to guide her along the way.

And so I sat down on my computer and googled the subject. I found lots of links, which I sent her, but was dissatisfied with the depth of what they were discussing. I could not find a comprehensive guide — from the point of view of a frontend newbie — to testing frontend applications. I could not find a guide that discusses both the theory and the practice, and is oriented towards testing frontend application.

So I decided to write one. And this is the third part of this series of blogs. You can find part one here, and part two here. Also, for the purpose of this blog, I wrote a small app — Calculator — that I will use to demonstrate the various types of testing. You can see the source code here. Note that it isn’t yet fully tested, but it will be once this series of blog posts will be done.)

End To End Testing

In part II, we’ve seen one end of the spectrum of testing — unit testing. We tested the core logic of the application, the module calculator, using mocha and this test module.

In this part, we’ll see the other end of the spectrum of testing — end to end testing, where we test the whole application together, and test it as a user would — essentially automating whatever the user does.

In our case, the calculator front-end app is the whole app — there’s no backend support, so in our case E2E testing means opening up the application in a real browser, doing a set of calculations using the keypad, and ensuring the value in the display is the correct one.

Do we need to check all permutations, like we did in the unit test? No! We already checked that in the unit tests. What we check in the E2E tests is not that this unit works, or that unit works, but rather that they all work together.

How Many E2E Tests

This is one reason why there should not be many E2E tests — if we tested correctly in the unit and integration tests, we have probably checked everything. The E2E test should just check that the glue binding all units works.

There is two more reasons why there are not many E2E tests. The first reason is that these tests are slow. Having hundreds of them, like you would have in unit and integration tests, means that running your tests would take a long time, and that is something that is imperative — tests should run fast.

The third reason to not have many E2E tests is that these tests tend to be flaky. Flaky tests are tests that usually pass, but sometimes fail. This cannot (usually) happen in unit tests, as they are usually simple input/process/output and involve mostly the CPU. But the more a test involves I/O (where in I/O I mean anything that is not CPU or memory driven), the more flaky it becomes. Can we mitigate the flakiness? Yes, to a bearable amount of flakiness. Can we kill flakiness entirely in E2E tests? Maybe, but I’ve never seen it happen.

So to remove the flakiness from our tests, have as few E2E tests as possible. Have one to 10 tests, each one testing the main flows of your application. Don’t try to do more with the tests, unless you absolutely have to.

Writing Frontend E2E tests

OK, already. Let’s get down to the business of writing frontend E2E testing. For our frontend E2E, we need to run two things: (i) a browser, and (ii) a web server that serves our frontend code.

Since we use Mocha for our e2e tests, just like for our unit tests (they all run together!), we will set up the browser and the web server using Mocha’s before hook, and clean them up using Mocha’s after function. The before and after hooks run before and after all the tests are run, and are used to setup up stuff that the tests functions themselves can use.

Let’s first look at setting up a web server.

Setting Up a Web Server in Mocha

A web server in Node? Immediately, express comes to mind, so without further ado, let’s see the code in our test:

In the setup, in the before hook, we create an express app, point it to our dist folder, and make it listen on 8080. The teardown, in the after hook, closes down the server.

What’s this dist folder? Well, it’s where we bundle our JS files (using webpack), and to where we copy our HTML and CSS. You can see that we do this in the npm build script in the package.json:

This means that for e2e tests, we need to remember to npm run build before we npm test. See how inconvenient that is? We didn’t need to do that for the unit tests because they run under node and don’t need transpiling or bundling.

For completeness’ sake, let’s look at the webpack.config.js that tells webpack how to do the bundling:

Webpack will read our app.js and bundle all the files it requires (recursively) into the bundle.js in the dist folder.

This dist folder is used both in production, and as we saw, in our E2E tests. This is important — E2E tests run in an environment that is as similar to production as possible.

Мастер Йода рекомендует:  Как создать шаблон для Joomla самостоятельно

Setting Up a Browser in Mocha

Now that we have the backend setup, and our application is served, all that is left is to run the browser so we can start to drive it on our application. Which package shall we use to drive the automation? I typically use selenium-webdriver, as it is a popular package.

First, let’s see how we use the driver, before we understand how to set it up:

In the before, we prepare the driver, and in the after, we clean it up. Preparing the driver will run a browser (Chrome, as we will shortly see), and cleaning up will close the browser. Note that preparing the driver is asynchronous, and returns a promise, so we use the new async/await functionality to make the code look nicer. (Hurrah for Node 7.7 — the first version to natively support async/await!)

And, finally, in the test function, we browse to the site https://localhost:8080, again using await, given that driver.get is an asynchronous function.

So how do those prepareDriver and cleanupDriver look like?

Oh, my. This is heavy-duty stuff. And I have to admit something — this code was written in blood. (Oh, and it works only in Unix systems — if any of you Windows people want to make it work in Windows, I’ll be happy to accept a pull request…). That means that it was written through a combination of Google/Stack Overflow/webdriver documentation, and heavily modified by experience. But it works!

Theoretically, you can just copy/paste it into your tests, without understanding, but let’s dig into it for a second.

The first two lines bring the webdriver, and it’s companion browser driver. The way Selenium Webdriver works is by having an API (in the module selenium-webdriver that we import in line 1) that works with any browser, and it relies on browser drivers to… drive the various browsers. The driver browser I used is chromedriver, imported in line 2.

The chrome driver does not need Chrome on the machine — it actually installs its own Chrome executable when you do npm install. Unfortunately, for some reason that I cannot understand, it can’t find it, and the chromedriver directory needs to be added to the PATH (this addition to the path is what doesn’t work in Windows). This we do in line 9. We also remove it from the path in the cleanup phase, in line 22.

So we’ve set up the browser driver, and it’s time to setup (and return) the web driver — which we do in lines 11–15. And because the build function is asynchronous and returns a promise, we await it.

Why do we do those things in lines 11–15? Why those exactly? That’s part of what’s written in blood. The reasoning is shrouded in the mists of the past, and cloaked in the mantle of experience. Feel free to copy/paste — no guarantees attached, but I’ve been using this code for a while now with no problems.

The Test! The Test!

We’re done setting up — it’s time to look at the code that uses the webdriver to drive the browser and test our stuff.

Let’s bring the test code here, but let’s do it slowly, because it’s a mouthful.

We’re skipping the setup, which we saw earlier, and diving into the test function itself.

The code here browses to the calculator app, and checks that the title is “Calculator”. Let’s see this:

Line 1 we already saw — we ask the driver to browse to our app. And we do not forget to await it.

Let’s for a second skip to line 9. In it, we ask the browser to return to us the title of the browser (await-ing the answer because it’s asynchronous), and then, in line 10, we check that it has the correct value.

So why do we retry this, using the promise-retry module? The reason is very important, and we will see the same thing happening in the rest of the test — when we ask the browser to do something, like navigating to a URL, the browser will do it, but asynchronously. Don’t let the await fool you! We are awaiting for the browser to say — “OK, I’ll do it”, and not for the operation to end.

Unfortunately, if you do not retry in this case, it could still work, because the browser is very fast. But try running it in a CI system like Travis, as I did, and you may get a failure, because CI-s are usually slower than your local system.

And that is why, in E2E tests on the browser, we need to retry our checks.

Looking at Elements

Onward and upward to the next part of the test!

The next thing we check is that, initially, the display is “0” (that’s a zero…). To do that, we need to find the element that holds the display, which in our case has the class display. This we do in line 7 — webdriver’s findElement returns the element we look for. And we can look for By.id, or By.css, or some other stuff. I usually use By.css — which accepts a selector and is very versatile, although By.javascript is probably the most versatile.

(and if you hadn’t noticed, By is imported from selenium-webdriver at the top).

Once we have the element, we can use getText() (you can use other functions on elements, too) to get at the text and check its value in line 10. And don’t forget:

Driving the UI

It’s time to drive the application — to click on the digits and the operators, and to check that the calculation is as we expect:

There we go — we first find the elements that we want to click, in lines 2–4. Then we click on them, in lines 6–7 (the calculation, if you can’t follow, is “42*2=”.

Then we retry until we get the correct value — “84”.

Running The Tests This Far

So we’ve got E2E tests, and unit tests. Let’s run them all using npm test:

Running E2E and unit tests together

Some Words on the use of await

If you look at a lot of samples out there on the web, you will see they do not use async/await, or even wait on the result using promises. Nope, they write code that is synchronous. How does this work? Honestly, I don’t know, but it looks like some weird shenanigans going on inside the webdriver. And, as even selenium says, this was an interim solution, until Node gets async/await support.

Well, guess what happened?

Some Words on the Documentation

Selenium’s documentation is, well, Java-ish. And that was not a compliment. On the other hand, the information is there. So bear with it, after a few tests, you will get it.

Next Week

We’ve seen the tests at the other end of the spectrum of tests — E2E tests that test it all. Next week, we’ll go back to the middle.

Summary

What did we see this week?

  • We saw hairy code that sets up the browser. Luckily, we need to write it only once.
  • We saw how to use the webdriver API to drive the browser and get at DOM elements and their values.
  • We used async/await extensively, as all the webdriver API is asynchronously using promises.
  • We understood why we need to retry stuff in E2E tests.


Е2Е тестирование Койна

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

Сейчас в нём три важных сценария: вход в приложение , создание бюджета и запись траты или дохода. Логика покрыта юнит‑тестами , но этого мало. Хочется быть уверенным , что если сценарии где‑то сломаются , то я об этом узнаю сразу. Поэтому для Койна я пишу ещё и Е2Е тесты.

Инструменты

End‑to‑end ( Е2Е ) тесты — это интеграционные тесты , которые взаимодействуют с интерфейсом так , как это делал бы пользователь. Для них я попробовал несколько инструментов , но больше всего мне понравился Сайпрес.

После его установки и запуска в корне проекта появляется папка cypress/. Внутри неё: integration/ — там находятся сами тесты , и support/ — там вспомогательные функции ( об этом подробнее дальше).

Вход в приложение

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

Исхода у сценария два: успешный и неуспешный вход. Пишем тест на первый случай.

Нам надо зайти в приложение и попасть на страницу логина. На страницу мы зайдём с помощью команды visit, передав аргументом адрес:

Проверим , существует ли форма логина , и пустое ли поле ввода. Проверяем наличие нужных блоков и то , что поле пустое:

Выборка элементов в Сайпресе работает похоже на Джейквери. Например , здесь мы выбираем элементы по классам. Метод should проверит , что на странице только 1 элемент с классом login, а элемент с классом login‑code пустой.

Набор текста в настоящих полях ввода в Сайпресе делается через метод type. Но в Койне клавиатура ненативная и настоящих полей ввода там тоже нет. Вместо них — блоки , в которых отображается « набранная» последовательность. Чтобы набрать какой‑то код на нашей клавиатуре , надо « нажать» клавишу с нужной цифрой. Мы будем разбивать код на символы и нажимать на клавиши с указанными символами.

Метод contains ищет элемент , который содержит переданный в аргументе текст , в нашем случае — символ. Метод closest находит ближайшего родителя с указанным селектором , в нашем случае — классом button.

Когда код набран , можно нажать на красную кнопку , чтобы « отправить» код.

Код теста целиком будет выглядеть так:

После запуска Сайпрес запустит браузер , прогонит сценарий и покажет , прошёл тест или нет. Выглядит это так:

Рефакторинг и второй сценарий

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

Команды похожи на плагины. Вы описываете функцию‑команду , и она становится доступной глобально через cy. Команды хранятся в папке support/, их можно как угодно разделять по файлам. Главное — импортировать их в support/index.js, чтобы Сайпрес их увидел.

Адрес страницы‑приложения меняться не будет , поэтому вход в приложение вынесем в команду enterApp, а сам адрес запишем в fixtures/common.json:

Проверка формы тоже будет повторяться , поэтому вынесем её в команду appContainsEmptyLoginForm.

Я предпочитаю называть команды либо:

  • глаголом с действием , которое надо выполнить: enterApp;
  • предикатом для проверок: appContainsEmptyLoginForm.

Первые ничего не проверяют , а лишь выполняют какое‑то побочное действие. Вторые проверяют то , что описано в названии.

Ввод чисел на клавиатуре нам тоже понадобится в других тестах приложения. Поэтому его мы превратим в команду keyboardType.

Нажатие на « энтер» нам тоже пригодится в других местах:

В итоге код теста станет таким:

Теперь напишем тест на неправильный код:

Как мы видим , первые две строки повторяются , поэтому их можно вынести к сетап теста:

После запуска увидим такую картину:

Если какой‑то тест свалится , то в логах будет описано , что не совпало. Сайпрес офигительно описывает ошибку: показывает , какой узел её вызвал , что не совпало , и сохраняет снапшот состояния. То есть можно ткнуть мышью в этот баг , и справа отобразится состояние страницы в это время!

Сценарий создания бюджета

Второй сценарий — создание бюджета. Чтобы заполнить новый бюджет , нужно открыть его настройки , ввести сумму , срок и сохранить. Проверим каждый шаг.

Заведём бюджет из 10000 попугаев на 10 дней. Приложение запишет в бюджет только 95% от той суммы , которую вводим , чтобы план не оказался впритык. Значит , после сохранения бюджет будет содержать 9500 попугаев.

Дальше выбираем срок. Мы выберем 10 дней , поэтому нам надо выделить 10‑й пункт в крутилке с датами. Проверяем , что даты до выбранной включительно стали красными и что в бюджете появилась строка с суммой на день.

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

И что сохранилась запись в истории о создании бюджета:

После запуска увидим такую картину:

Основной сценарий трат

Теперь проверим сценарий пользовательских трат. Есть два варианта трат: когда бюджета ещё нет , и когда он задан. Чтобы отделить набор тестов для первого случая от набора для второго , будем использовать context.

Каждую трату мы можем сделать полезной или вредной. Чтобы проверить все случаи и не повторять код , напишем функцию spendMoneyOnce, которая будет заниматься тратами.

И функцию , которая будет проверять , сохранилась ли трата:

Тогда тестирование трат в категориях будет выглядеть следующим образом:

Траты из заполненного бюджета

Теперь протестируем трату , когда бюджет задан. У меня описано много сценариев , но здесь я покажу два. В первом трата меньше дневного лимита , и сумма на день остаётся такой же , во втором — трата больше , и сумма на день уменьшается.

Функция testSpendWithActiveBudget берёт на себя алгоритм проверки. Она совершает трату , проверяет , что трата записалась , затем проверяет , должна ли была сумма на день пересчитаться. Если да , то проверяет новую сумму на день. Если нет , то проверяет остаток на сегодня — разницу между дневным лимитом и суммой трат за сегодня.

Меняем даты в браузере

Осталось протестировать , как себя ведёт бюджет и история , если приложение запускают через день или несколько дней.

Сперва проверим , что непотраченные деньги попадают в копилку:

Затем , что сумма на день осталась той же , если пользователь не вышел за вчерашний лимит

И что сумма уменьшится , если пользователь вышел за лимит:

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

Вначале я создаю базовую команду skipDays, которая будет принимать количество дней для пропуска и момент времени , от которого отсчитывать. Внутри команды работает cy.clock, который описывает изменение времени.

Первым агументом ему передаём таймштамп момента , в который надо перевести часы. Вторым — функции и объекты , которые будут изменены во время выполнения. Нам достаточно подменить только объект Date.

Между тестами сбрасывать настройки времени через restore необязательно , потому что Сайпрес делает это сам. Но если мы запускаем skipDays внутри одного теста несколько раз , то чтобы перетереть предыдущие настройки , надо вызвать restore.

Метод reload перезагружает страницу — будто пользователь заходит в приложение спустя указанное время.

Результат

Самое кайфовое в таких тестах — смотреть , как они работают. Вот ускоренный видосик со всеми тестами , которые есть в проекте. Самые глазастые смогут там найти настоящий рабочий пароль для входа в приложение

Знакомство с фронтенд-тестированием. Часть третья. E2E-тестирование

Тестовое задание на позицию frontend-разработчик

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

Написать функцию dscount , которая подсчитывает количество идущих подряд символов s1 и s2 в строке, без учёта регистра. Функция должна пройти следующие тесты, как минимум:

Данный код — для примера и не обязателен к использованию в таком виде. Можно вносить модификации.

  • Обратите внимание на производительность вашего решения.
  • Решение должно быть компактным.
  • Решение должно быть простым, умещаться в 1м файле и содержать не более 20 строк кода.

Идеальное время решения на задачу не более 15 минут (задачу можно решить и за 5 минут).

Реализовать функцию checkSyntax(string) , проверяющую на синтаксическую верность последовательность скобок. Задача не сводится к простой проверке сбалансированности скобок. Нужно еще учитывать их последовательность (вложенность).

  • Обратите внимание на производительность вашего решения.
  • Решение должно быть компактным.
  • Решение должно быть простым, умещаться в 1м файле и содержать 20-30 строк кода или меньше.

Идеальное время решения на задачу не более 30 минут (задачу можно решить и за 10 минут).

Покажите решение, если нужно проверять следующий набор скобок:

Изменится ли ваше решение, если нужно проверять только такой набор скобок:

  • В случае ошибки возвращаем 1.
  • В остальных случаех возвращаем 0.

Тесты для 1го набора:

  • Есть 2 сковороды для оладьев, каждая из которых вмещает ровно по 1 блинчику за 1 раз.
  • Есть 3 панкейка (блинчика), которые надо пожарить.
  • За 1 минуту жарится 1 сторона блинчика.
  • Блинчики надо обжарить с 2х сторон.

Итерацией считать процесс жарки 1й стороны 2х блинчиков на 2х сковородах. Сколько нужно времени (итераций) при оптимальном распределении чтобы пожарить 3 панкейка?

Релизуйте ваш алгоритм в виде кода. Это может быть как ООП код, так и функциональный и даже процедурный. Выбор подхода обоснуйте.

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

Задачи на работу с чужим кодом.

Посмотрите на код:

Что можно улучшить? Как бы вы его переписали?

Что можно улучшить? Как бы вы переписали функцию drawRating при условии что на вход функции drawRating должна приходить переменная vote, содержащая значение от 0 до 100. Интересует именно логика реализации функции, не визуальное отображение звезд.

Реализуйте функцию parseUrl(string) , которая будет парсить URL строку и возвращать объект с распарсенными данными. Пример:

Желательно задачу решить как можно меньшим числом строк кода и затратив на реализацию минимум времени. Подсказка: JS-ninja может решить эту задачу за 1 минуту и написать 1 строчку кода. Дерзайте =)

Необходимо разработать javascript-компонент для сортировки таблиц с данными.

Функционал

  • Сортировка по столбцам: при нажатии на название столбца строки таблицы сортируются по возрастанию, при повторном клике — по убыванию. Графическим элементом или текстовым сообщением указывается направление сортировки.
  • Клиентская пагинация: данные необходимо отображать постранично, максимум 50 элементов на страницу. Необходимо предоставить пользовательскую навигацию для перехода по страницам.
  • Фильтрация: компонент предоставляет текстовое поле, в которое пользователь может ввести текст и строки таблицы, данные которых не содержат подстроку, введённую пользователем, скрываются. Перефильтрация осуществляется по нажатию на кнопку Найти.
  • По клике на строку таблицы значения полей выводятся в дополнительном блоке под таблицей.
  • Данные в таблицу загружаются с сервера. Способ загрузки с сервера на ваш выбор.

Для демонстрации работы компонента необходимо сделать простую HTML страницу. Пользователю предлагается выбрать набор данных: маленький или большой. При выборе набора данных он загружается с сервера и по данным строится таблица.

Формат данных от сервера

Сервер возвращает JSON-массив данных. Пример данных:

Маленький объем данных берется по ссылке https://www.filltext.com/?rows=32& >

Большой объем данных берется по ссылке https://www.filltext.com/?rows=1000& >

Замечания

  • Особое внимание следует уделить скорости работы. Зависание интерфейса при выполнении операций загрузки данных, фильтрации, сортировки недопустимо.
  • Во время загрузки данных стоит показать какой-то индикатор
  • Разрешённые библиотеки: jQuery, Lodash/Underscore, Backbone, самописный. Но вам придется объяснить выбор и причину использования. Использование сторонних библиотек будет плюсом только в случае если это оправданно и вы сможете объяснить причину выбора. Показав свои знания в грамотном применении сторонних готовых решений, вы имеете шанс повысить свою профессиональную привлекательность для нас.
  • Пишите код так, как бы вы его писали в работе — внутренности задания будут оцениваться даже тщательней, чем внешнее соответствие заданию. Код должен быть организован так, чтобы его можно было заново использовать.
  • Помните про обработку ошибок!
  • Верстка может быть самая простая. Визуализацию и украшение делаете на ваш вкус. Мы не против использования https://getbootstrap.com/ или похожего UI фреймворк, но только для UI представления (нельзя использовать JS код для решения задачи, но можно использовать для оформительских эффектов(анимации и тому подобное))!

Дополнительным плюсом будет:

  • Использование TypeScript или ES6+(babel).

Схема визуального представления данных

Если выбран пользователем с >

Дополнительно напишите нам, как вы тестировали результат своей работы. Какие используете инструменты и как вы осуществляете тестирование.

Результат выполнения задания нужно будет оформить здесь же, на гитхабе. В качестве ответа не нужно присылать никаких(!) ZIP архивов и наборов файлов. Все ваши ответы должны быть оформлены на https://github.com/ . Вы присылаете только ссылку на ваш репозиторий. У нас в компании применяется GIT, и если вы его не знаете, вам стоит освоить базу самостоятельно. Если у вас еще нет аккаунта, то это хороший повод его завести.

Если есть вопросы, вы всегда их можете задать, связавшись с человеком, который выдал вам задание.

Как мы делаем проекты: тестирование

Если вы пропустили

Участники со стороны Лайв Тайпинг:


  • тестировщики;
  • руководитель проекта;
  • разработчики.

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

  • удобство интерфейса, оно же юзабилити;
  • фронтенд;
  • бэкенд.

Тестирование юзабилити

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

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

Тестирование фронтенда

Процесс строится следующим образом: тестировщик получает от разработчика актуальную сборку приложения или рабочую версию , запускает проект на эмуляторе или в браузере и вручную или автоматически выполняет .

Автоматизированные тесты помогают в случае, когда на проекте высокая частота релизов; раза в неделю — для тестировщика это тяжёлая задача, зато покрытие автотестами самых критичных пользовательских сценариев позволяют держать такой темп.

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

Мастер Йода рекомендует:  12 лучших блогов за историю рубрики #blogs

Решение об автоматизации должно приниматься осознанно. Необходимо в первую очередь ответить на вопрос, какую проблему автоматизация должна решить, сколько ресурсов будет на неё потрачено (это такая же разработка — автотесты требуют поддержки, написать и забыть не получится) и за какой срок эти вложения окупятся. В качестве средств тестирования приложений у нас используются Katalon Studio и Selenium IDE для веба и Appium для мобильных платформ.

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

Чтобы проверить работоспособность написанного кода, нужно провести функциональные, интеграционные и регрессионные тесты.

Функциональные тесты

Они проводятся для проверки работы реализованных функций продукта.

В функциональном задании, которое уже должно быть утверждено к этапу разработки, описаны функции продукта. Задача разработчика — их реализовать, а тестировщика — составить тестовую документацию (, ) для их проверки.

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

Регрессионные тесты

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

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

Чем дальше заходит разработка, тем больше функций нужно проверять и тем больше времени занимает регрессионное тестирование.

Тестировщик готовит план тестирования приложения на итерацию в зависимости от того, какие задачи нужно будет реализовать, и договаривается о расписании регрессов. Оно может быть основано на календаре, либо при достижении определённого объема выполненных задач, либо при реализации конкретной критичной задачи. После регрессионного тестирования тестировщик находит некоторое количество багов, правит их и проводит ещё одну итерацию тестирования. В среднем, для того чтобы все значительные ошибки были исправлены, проводится такие итерации.

Интеграционные тесты

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

Приёмочное тестирование

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

Тестирование бэкенда

Что будет, если на сервер вдруг разом обрушивается огромное количество запросов? Зависит от того, насколько он устойчив, и именно это проверяется в рамках тестирования бэкенда. С помощью стороннего сервиса тестировщики искусственно создают ситуацию, когда число пользователей резко возрастает; они посылают большое количество обращений к серверу и наблюдают, на каком значении сервер «упадёт», то есть перестанет отвечать на обращения. Если критическое значение слишком низкое, разработчики предлагают меры по оптимизации сервера. Это называется нагрузочным тестированием.

Но провести нагрузочное тестирование только силами отдела тестирования обычно невозможно. Cначала нужно определиться, что и как тестировать. На вопрос «что» обычно помогают ответить разработчики; они знают, какие запросы чаще всего отправляются на сервер, какие из них передают больше всего информации и генерируют наибольшую нагрузку. А на вопрос «как» поможете ответить вы; вы знаете свой бизнес, знаете, на какую аудиторию он рассчитан, а если бизнес уже рабочий, то у него, скорее всего, есть пользовательские метрики: наиболее частые сценарии поведения в системе, периоды спада и возрастания активности, характер этих активностей, периоды и характер пиковых нагрузок. Без этих данных нагрузочное тестирование не даст реальной картины.

Результаты тестирования говорят о реальных и потенциально возможных нагрузках на систему. Также можно смоделировать ситуацию, когда система не выдержала и упала, и посмотреть, сможет ли она возобновить правильную работу самостоятельно или это придется делать руками. Такая проверка поможет подготовиться к подобным случаям в продакшене.

В качестве инструментов для нагрузочного тестирования мы используем Yandex. Tank. Он позволяет довольно быстро и просто начать тестирование, но при этом у него почти нет возможности проверить сложные сценарии (например, когда пользователь долго ходит по разделам сайта). Для таких вещей можно использовать. например, Apache jMeter.

Очень важно то, что отдел тестирования и отладки приложений Лайв Тайпинг не только ищет баги, но и может обнаружить непродуманность в UI или в принципе спорную функцию, чтобы оперативно обсудить на митинге и затем донести до вас.

Разработку без тестирования можно сравнить с рисованием вслепую. Элементы проекта далеко не всегда ведут себя в рамках ожиданий разработчика. Поэтому тестирование такое разное и сложное.

Участие Лайв Тайпинг в жизни проекта не кончается на релизе. Следующий этап — поддержка.

Знакомство с фронтенд-тестированием. Часть третья. E2E-тестирование

• Продукт у нас хороший, и вы бы сами убедились, если бы не…

Онлайн-тренинги

Конференции

Что пишут в блогах (EN)

September 26

Разделы портала

Про инструменты

Основы BDD: Юнит, Интеграционные и End-to-End тесты
20.04.2020 10:32

Перевод: Анна Радионова

Существует много видов ПО тестов. Практики BDD можно применять в любых аспектах тестирования, но BDD фреймворки используются далеко не во всех типах тестов. Поведенческие сценарии, по сути, являются функциональными тестами — они проверяют, что тестируемый продукт работает корректно. Для тестирования производительности могут использоваться инструменты, в то время как BDD фреймворки не предназначены для этих целей. Задача данной статьи, в основном, состоит в описании роли BDD автоматизации в Пирамиде Тестирования. Прочитайте статью BDD 101: Manual Testing для того, чтобы понимать как BDD применяется при ручном тестировании. (Всю информацию по BDD можно найти на странице Automation Panda BDD page)

Пирамида Тестирования

Пирамида Тестирования представляет собой подход к разработке тестов, который разделяет тесты на три слоя: юнит, интеграционные и end-to-end.

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

Интеграционные тесты — это тесты черного ящика, которые проверяют, что интеграционные точки между компонентами продукта работают корректно. Тестируемый продукт должен быть в активной фазе и развернут на тестовом окружении. Тесты сервиса часто являются именно тестами интеграционного уровня.

End-to-end тесты — это тесты черного ящика, которые проверяют пути выполнения системы. Их можно рассматривать как многошаговые интеграционные тесты. Тесты для веб-интерфейса часто являются именно end-to-end тестами.

Ниже представлено графическое отображение Пирамиды Тестирования:

Сложность тестов отображена по возрастанию: юнит-тесты самые простые и выполняются очень быстро, в то время как end-to-end тесты требуют тщательной настройки, продуманности логики и их прогон длится долгое время. В идеале лучше, чтобы большая часть тестов находилась в нижней части схемы, а в верхней ее части их было меньше. Реализовать тестовое покрытие и изолировать тесты проще на более низкоуровневом этапе, чтобы на более высоком уровне были внедрены более трудозатратные, хрупкие тесты. Перенос тестов в нижнюю часть пирамиды также может свидетельствовать о более широком тестовом покрытии и меньшем времени выполнения. Различные слои тестирования оптимизируют риски и делают его эффективным.

Тестовые BDD фреймворки не предназначены для написания юнит-тестов. Юнит-тесты являются низкоуровневыми, программными тестами для проверки отдельных функций или методов. Можно написать Gherkin тест в целях юнит-тестирования, но по факту это перебор. Гораздо лучше использовать уже существующие юнит-тест фреймворки как, например, JUnit, NUnit и pytest.

Тем не менее, BDD практики могут применяться для юнит-тестов. Каждый юнит-тест должен быть направлен на основную составляющую: один вызов, единичная вариация, определенная комбинация ввода; на поведение. В дальнейшем при разработке спецификация поведения фичи четко отделяет юнит тесты от тестов более высокого уровня. Разработчик функционала также ответственен за написание юнит-тестов, в то время как за интеграционные и end-to-end тесты несет ответственность другой инженер. Спецификация поведения является, своего рода, джентльменским соглашением о том, что юнит-тесты будут являться отдельной сущностью.

Интеграционное и End-to-End тестирование

Тестовые BDD фреймворки наиболее ярко проявляют себя на интеграционных и end-to-end уровнях тестирования.

Поведенческие спецификации однозначно и лаконично описывают, на что именно ориентирован тест-кейс. Шаги могут быть написаны на интеграционном или end-to-end уровне. Тесты сервиса могут быть написаны с помощью поведенческих спецификаций, как, например в Karate. End-to-end тесты фактически представляют собой многошаговые интеграционные тесты. Обратите внимание на следующий пример, который, на первый взгляд, кажется базовой моделью взаимодействия с пользователем, но, по сути, является объемным end-to-end тестом:

Given a user is logged into the social media site
When the user writes a new post
Then the user’s home feed displays the new post
And the all friends’ home feeds display the new post

Простая публикация в социальной сети включает процессы взаимодействия с интерфейсом, вызовы сервисов бекенда и внесение изменений в базу данных в режиме реального времени. Описан полный путь прохождения в системе. Автоматизированные шаги могут покрывать эти уровни явно или неявно, но они совершенно точно покрыты тестом.

Длительные End-to-End тесты

Термины часто понимаются разными людьми по-разному. Когда люди говорят “end-to-end тесты,” они подразумевают продолжительные последовательные тесты: тесты, которые покрывают различное поведение продукта одно за другим. Это утверждение заставляет содрогнуться приверженцев BDD, т.к это противоречит основному правилу BDD: один сценарий, одно поведение. Конечно, с помощью BDD фреймворков можно составлять длительные end-to-end тесты, но нужно хорошо подумать, стоит ли это делать и как именно.

Существует пять основных принципов написания длительных end-to-end сценариев в BDD:

  1. Не стоит беспокоиться на этот счет. Если BDD процесс поставлен правильно, то каждое отдельное поведение уже полностью покрыто тестовыми сценариями. Каждый сценарий должен покрывать все классы эквивалентности вводимых и выводимых данных. Таким образом, длительные end-to-end сценарии будут являться, в основном, дублированием тестового покрытия. Вместо того, чтобы тратить время на разработку, лучше отказаться от автоматизации длительных end-to-end сценариев, как от тех, которые не представляют большой ценности и уделить больше времени ручному и исследовательскому тестированию.
  2. Объединить существующие сценарии в новые. Каждая When-Then пара представляет собой индивидуальное поведение. Шаги из существующих сценариев могут быть переопределены в другие сценарии и для этого потребуется лишь незначительный рефакторинг. Это нарушает хорошие практики Gherkin и может привести к появлению длительных сценариев, но это наиболее практичный способ переиспользовать шаги для обширных end-to-end сценариев. Большинство BDD фреймворков не поддерживают пошаговый порядок, а если поддерживают, то шаги должны быть переписаны, чтобы код работал. (Этот подход является наиболее практичным, но менее традиционным.)
  3. Встраивайте проверки в Given и When шаги. Данная стратегия позволяет избежать дуплицирования When-Then пар и убедиться, что проверки производятся. Корректность каждого шага проверяется на протяжении всего процесса с помощью Gherkin текста. Однако, может потребоваться ряд новых шагов.
  4. Воспринимайте последовательность поведений как уникальное отдельное поведение. Это наилучший способ обдумывания длительных end-to-end сценариев, т.к. он усиливает поведенческое мышление. Продолжительный сценарий имеет ценность только в том случае, если он расценивается как уникальное поведение. Сценарий должен быть написан с целью подчеркнуть эту уникальность. В противном случае это не тот сценарий, который стоит использовать. Такие сценарии часто являются декларативными и высокоуровневыми.
  5. Не используйте BDD фреймворки и пишите тесты исключительно с помощью средств автоматизации. Gherkin предназначен для совместной работы в отношении поведения, в то время как продолжительные end-to-end тесты решают проблемы интенсивности работы QA. Бизнес может предоставить поведенческие спецификации, но никогда не станет писать end-to-end тесты. Переписывание поведенческих спецификаций в длинные end-to-end сценарии может блокировать разработку. Гораздо лучшим решением является сосуществование: приемочные тесты могут быть написаны при помощи Gherkin, а продолжительные end-to-end тесты — средствами программирования. Для автоматизации обоих наборов тестов можно использовать одну и ту же базу кода, у них могут быть единые модули поддержки и даже методы определения шагов.

Выберите подход, наиболее подходящий вашей команде.

Блог Makeomatic: разработка сайтов и мобильных приложений

Эффективное сквозное тестирование с Protractor

С AngularJS действительно удобно работать, поскольку тестам изначально придаётся большое значение — каждое изменение, внесённое в исходники, тестируется перед сохранением в ядро.

Тестирование крайне важно, особенно в Javascript — языке с динамической проверкой типов. Тесты позволяют отловить ошибки до того, как они попадут в продакшн. Уже не говоря о том, что они повышают качество кода.

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

Тестирование — неотъемлемая часть работы, если мы хотим понимать, что происходит в приложении.

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

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

Автоматизация с помощью Protractor

Protractor – рекомендуемый фреймворк для сквозного тестирования. В отличие от стандартного исполнителя сценариев Angular, Protractor сделан на основе Selenium WebDriver — инструмента для автоматизированного тестирования веб-приложений, с API и набором расширений, позволяющих управлять поведением браузера. Расширения WebDriver есть для всех типов браузеров, включая наиболее популярные. Таким образом, мы получаем быстрое и стабильное тестирование в реальной браузерной среде.

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

Установка

В отличие от стандартного исполнителя сценариев, для работы Protractor нужно запустить отдельный сервер по адресу https://location:4444 (можно перенастроить). К счастью, в дистрибутиве Protractor имеется утилита, упрощающая процесс установки Selenium Server. Чтобы воспользоваться скриптом, необходимо установить Protractor локально в корневой каталог тестируемого Angular приложения.

$ npm install protractor

Далее запускаем скрипт-загрузчик Selenium (расположен в локальном каталоге node_modules/ ) командой:

Этот скрипт загружает файлы, необходимые для запуска Selenium, и создаёт соответствующий каталог. Когда всё загружено, запускаем Selenium с драйвером Chrome командой старт:

Если у вас возникают проблемы при запуске Selenium, попробуйте обновить ChromeDriver, загрузив последнюю версию здесь.

Теперь можем работать с Protractor, подключившись к серверу Selenium, который работает в фоновом режиме.

Настройка

Наподобие таск-раннера Karma, для запуска и работы с Selenium нужен конфигурационный файл. Наиболее простой способ создания конфигурационного файла Protractor — скопировать базовую конфигурацию из каталога установки.

$ cp ./node_modules/protractor/example/chromeOnlyConf.js protractor_conf.js

Для старта Protractor в файле необходимо сделать несколько изменений. Во-первых, по умолчанию конфигурационный файл использует драйвер Chrome, которого нет в текущей директории. Поэтому мы должны указать путь к драйверу в локальной папке ./node_modules .

Далее нужно изменить путь массива specs, указав на наши локальные тесты.

При настройке тестов в Protractor существует множество различных параметров конфигурации. В этой статье мы затронем некоторые из них, но помните, что существует и немало других опций.

Есть два варианта запуска тестов. Первый, автономный режим — использовать Protractor для запуска Selenium, когда запускаем наши тесты. Пример файла конфигурации в данной статье использует этот метод.

Второй вариант предполагает подключение к отдельно запущенному серверу Selenium. Когда тесты становятся более сложными, вероятно, целесообразнее использовать этот вариант.

В этом случае нужно удалить указанные выше настройки (chromeOnly и chromeDriver), и добавить параметр seleniumAddress, указывающий путь к запущенному серверу Selenium:

Написание тестов

При написании тестов в Protractor используется фреймворк Jasmine. То есть мы пишем тесты точно так же, как для Karma. Пример простого теста в Protractor:

В примере приведён не полный код, но структура узнаваема — синтаксис Jasmine. Для создания структуры тестов используем функции beforeEach() , afterEach() и вложенные блоки describe() . Для выполнения тестов используем синтаксис Jasmine – expect() .
При написании тестов в Protractor нам понабодятся некоторые его глобальные переменные. Ниже приведены некоторые из них.
browser – оболочка вебдрайвера, используется для навигации и получения информации о странице.

Browser

Можем использовать переменную browser для перехода на страницу с помощью функции get() :

Для использования этого теста в отладчике node, запускаем его в режиме отладки:

$ protractor debug conf.js

Запуская Protractor в режиме отладки, мы получаем бонус — выполнение в браузере останавливается, и теперь все клиентские скрипты Protractor доступны нам из консоли. Чтобы получить к ним доступ, нужно вызвать объект Protractor – window.clientSideScripts.

Начнём тестировать!

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

Наше приложение

Давайте для примера возьмём приложение, реализующее свой вариант просмотра Github issues. Простое приложение, у которого всего несколько функций:

  • Оно позволяет указывать владельца репозитория и URL через окно ввода;
  • В нём имеется главная страница и страница About;
  • issues упорядочены друг под другом;
  • используется Gravatar пользователя.

Наше конечное приложение выглядит вот так (картинка 1 a, b — из статьи):

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

Стратегия тестирования

По опыту, наилучший баланс между написанием тестов и написанием кода достигается тогда, когда вы чётко понимаете, что тестировать и как именно тестировать. Когда мы пишем тест, важно, чтобы он проверял именно то поведение, которое задумано. То есть, мы не должны писать тест, чтобы проверить, меняется ли содержание тэга h1 , потому что мы вводим текст в поле . А вот проверить фильтрацию нашей фичи «поиск в режиме реального времени» (Live Search) нужно.

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

Наконец, нужно структурировать тесты так, чтобы каждый блок проверял какую-то свою, очень маленькую задачу. В идеале тест должен проверять не более 1 ожидаемого события.

Ну, хватит теории, применим стратегию на практике.

Во-первых, нужно проверить, что наше приложение добавляет название репозитория, с которым мы будем работать. Используя функционал Angular, приложение делает http запрос к github.com . Этот запрос возвращается, и мы заполняем оставшиеся поля главной страницы.

Во-вторых, необходимо протестировать изменение навигации и содержания страницы. Этот тест предполагает нажатие навигационной кнопки для изменения местоположения $location .

Создаём первые тесты

Наш файл конфигурации Protractor довольно прост и почти не отличается от базового конфига, который поставляется при установке:

Будем писать тесты в папке test/e2e , как и указали в конфигурационном файле в виде [name].spec.js . Создадим наш первый тест под названием main.spec.js .

Набросаем простую «рыбу»:

Тк мы пишем тесты на фреймворке Jasmine, воспользуемся блоком beforeEach() . Также необходимо отслеживать экземпляр Protractor, создадим для этого переменную ptor . Для каждого теста будем использовать объект browser для перехода на главную страницу.

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

Вместо того, чтобы указывать полный URL каждый раз, когда хотим протестировать страницу, можем добавить параметр baseUrl в конфигурационный файл Protractor. Далее будем считать, что этот параметр в конфиге у нас выглядит так:

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

Получив нужный элемент, с помощью Protractor метода isElementPresent() : зададим ожидание, проверяющее присутствие элемента на странице:

Для тестирования необходимо запустить сервер Selenium. К счастью, webdriver-manager облегчает нам жизнь (эта утилита автоматически включена в Protractor). Итак, запускаем webdriver-manager :

Запускаем Protractor в новом терминале. Двоичный файл Protractor принимает один аргумент — конфигурационный файл:

Тестируем поле ввода

Приступим к тестированию . Главная страница загружает одну форму с одним полем ввода, которое отображается, если пользователь сам не выбрал репозиторий для поиска issues. input type=»text» привязан к модели repoName . После заполнения, форма исчезает и появляется нужный список issues.

HTML выглядит так:

Нам важно протестировать, что заполненная форма исчезает, и на её месте появляется нужный список issues. Поэтому в следующем тесте выбираем элемент и заполняем его, для этого применим к нему метод sendKeys() . Для выделения самого элемента input воспользуемся методом by.input() , который найдёт элементы , содержащие привязку к ng-model :

При прогоне этого теста увидим, что поле заполняется. Никаких ожиданий нет, тк мы их ещё не написали, но видим, что в поле ввода появляется angular/angular.js .

Чтобы поле ввода исчезло, нужно отправить заполненную форму. Наиболее просто это сделать, сымитировав нажатие клавиши enter. Поэтому в методе sendKeys() добавили сочетание \n , которое имитирует нажатие enter в элементе .

Теперь осталось написать ожидание, что элемент repoForm более не существует на странице (тк мы прячем его с помощью ng-if ).

Используем для этого упомянутый ранее метод:

В части 2 мы протестируем список и навигацию. Держите руку на пульсе!

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