Тестирование в Agile. Среда виртуализации Vagrant + Docker
Меня зовут Владимир Сидоренко, и я работаю старшим инженером по качеству в DataArt.
Расскажу о том, была организована работа в нашем проекте, с какими трудностями мы столкнулись и как мы их преодолели. Мы увидим, какова роль тестировщика в Аgile-проектах, узнаем, как сделать так, чтобы тестирование не стало узким местом, замедляющим весь процесс. Рассмотрим, как быстро и безболезненно перейти из Scrum в Канбан. Также мы поговорим об использовании средств виртуализации для тестирования. Все это — на основе нашего опыта.
Входные данные проекта
Мы разрабатывали веб-портал, собирающий информацию из мобильных и десктопных приложений. Входные данные нашего проекта были следующими:
- Тип: веб-проект;
- Методология: Scrum, затем — Канбан;
- Команда: 10 человек;
- Инструменты:
- Jira;
- GitHub;
- Vagrant + Docker + Virtual Box (системы виртуализации — очень полезные инструменты для тестирования);
- Языки:
- Java;
- JavaScript (Angular).
Как был организован процесс
Сначала мы работали в соответствии с эталонной Scrum-моделью.
Клиент выделял нам человека (product owner — «владелец продукта»), который отвечал за составление приоритезированного списка задач (backlog), которые мы должны были выполнить. Затем мы планировали спринт — Scrum-мастер помогал нам выбрать главные задачи из списка, которые мы были обязаны выполнить во время спринта. Спринты у нас были трехнедельные. По окончанию спринта подводились итоги: мы смотрели, сколько задач выполнили и сколько — не успели; также считались разные показатели, оценивающие производительность работы. На ретроспективе спринта обсуждали, что сделали правильно, а что пошло не так, и пробовали сделать выводы, которые позволят избежать повторения ошибок в предстоящем спринте.
Артефакты
Теперь рассмотрим вкратце артефакты, которые мы использовали.
Term | Definition |
User Stories | Something a user wants and associated acceptance criteria. For example, «As a Facebook user, I need an ability to retrieve a link to someone’s else entry so that I can share it via other social networks or send by mail to my friends» |
Epics | A collection of User Stories |
Backlog | A list of requirements for a Product or Sprint |
Пользовательские истории (user stories) —то, что от нас хочет клиент, записанное вместе с соответствующими приемочными критериями. Например, пользовательская история может выглядеть так: «Мне как пользователю Facebook нужна возможность получать ссылку на чью-либо запись, чтобы я мог делиться ей через другие соцсети или отправлять ее по электронной почте друзьям». Пользовательские истории создаются владельцем продукта и записываются в Jira.
Эпики (epics) — связанные друг с другом пользовательские истории, собранные в большую коллекцию. Когда все задачи в эпике выполнены, можно считать, что функционал в основном сделан.
Backlog — список требований к продукту или к спринту, то есть список задач.
Собрания и церемонии
Какие собрания и церемонии обеспечивают Scrum-процесс?
Term | Definition |
Sprints (Iterations) | Time-boxed effort to create a «Done» product Increment |
Sprint Planning | Time-boxed effort to plan the Sprint by the Scrum team |
Grooming | «Quick» sprint pre-planning , where the whole team required |
Daily Stand-ups | Time-boxed effort to share the status, usually takes 10 minutes |
Sprint Retrospective | Time-boxed review of the Scrum Team’s performance during the last Sprint in order to improve in the next Sprint |
Что такое спринт и планирование спринта, понятно.
Груминг — по идее, очень быстрое предварительное планирование, в котором участвует каждый член команды. Но на самом деле у нас груминг часто отнимал слишком много времени — по моему мнению, вместо него достаточно простого планирования спринта.
Ежедневные слеты (daily stand-ups) — очень быстрые ежедневные совещания, буквально на 10 — 15 минут. На них каждый сообщает, что сделал вчера и собирается делать сегодня. Это важный элемент работы.
Что такое ретроспектива, уже было сказано.
Доска
Для контроля работы в Jira заводится Scrum-доска, по которой мы перемещаем задачи в зависимости от статуса выполнения. Доска позволяет очень ясно увидеть, что сейчас происходит в проекте и кто чем занят. Вот так она у нас выглядела:
ToDo | In Dev | Dev Review | Ready for Test | In Test | Test Review | Ready for Merge | Do not merge (On Hold) | Done |
Ticket 6 | Ticket 3 | Ticket 2 | Ticket 1 | Ticket N | ||||
Ticket 5 | Ticket N | |||||||
Ticket 4 | Ticket N |
В «ToDo» у нас заносится список задач из бэклога при планировании спринта. При этом Scrum-мастер смотрит, насколько производительно работала команда в предыдущие спринты, и в зависимости от этого решает, сколько задач можно поставить в ToDo. Каждая задача оценивается — насколько тяжела и сколько времени займет выполнение.
Как только работа над задачей начинается, мы перетаскиваем задачу в соседний столбец — «In Dev».
После того как задача выполнена, она переносится в «Dev Review». Dev Review — неотъемлемая часть гибкой методологии разработки. Это этап, на котором другой разработчик смотрит код, делает замечания и вносит поправки. Уже на этом этапе можно устранить много ошибок, перед тем как код пойдет на тестирование.
После «Dev Review» задача отправляется в раздел «Ready for Test», и, как только мы начинаем тестирование, задача переносится в «In Test». У нас на всю команду было всего два тестировщика, поэтому, когда в раздел «Ready for Test» попадало 6 — 7 задач, на этом этапе работа часто замедлялась — тестирование было узким местом, и поэтому остальным приходилось нас ждать. Они работали уже над следующими задачами, а мы возились со старыми. Как мы решили эту проблему, расскажу далее.
После «In Test» задача попадает в раздел «Test Review». Test Review — очень полезная процедура, которую осуществляет либо QA-менеджер на стороне клиента, либо владелец продукта. Мы приводим доказательства, что тесты прошли успешно: предоставляем логи, скриншоты, видео. Это нужно, чтобы клиент понял, что выполненная работа соответствует приемочным критериям, и задача выполнена успешно. Если этого не делать, есть риск, что клиенту что-то не понравится в коде, который пошел в конечную версию продукта. Таким образом, мы избавляемся от конфликтов с клиентом.
И наконец — «Ready for Merge». Тут уже код сливается с главной веткой, и все довольны, пока не находятся новые ошибки.
Роль тестировщика и процесс тестирования
Если вкратце, тестировщик при работе в такой методологии выполняет следующие действия:
— Общение с клиентом и выяснение требований;
— Запись приемочных критериев в Jira;
— Исследовательское и регресcионное тестирование;
— Написание тестов;
— Предложение новых идей;
— Модульное и приемочное бэкенд-тестирование. Модульные и приемочные бэкенд-тесты пишут разработчики, и тесты прогоняются во время сборки CI (continuous integration) билдов.
Детальное выяснение требований представляет собой так называемые kick-off — достаточно длинные (как правило) созвоны, в которых мы уточняем пользовательские истории. Дело в том, что иногда в них изначально слишком мало сведений, чтобы начать разработку — а нам, тестировщикам, нужно сразу же точно сформулировать, что должно получиться на выходе. Kick-off позволяют тестировщику понять, что именно хочет клиент. Так тестировщик выясняет, упорядочивает и записывает требования к тому, что должно быть разработано.
Затем тестировщики оформляют приемочные критерии: их мы заносим в нашу Jira, записывая на языке Gherkin. Запись на таком языке может выглядеть следующим образом:
Scenario 1— login page
Given user is on the Login Page
When user fills in the correct credentials in the input fields
And clicks on Submit button
Then he is redirected to the home page
Такой способ записи очень удобен для UI приемочных критериев UI — если мы его используем, потом очень удобно писать автоматические приемочные тесты. Тестировщик просто немного исправляет эти записи под себя и пишет код на их основе в соответствии с выделенными шагами. Однако не всегда приемочные критерии можно записать в Gherkin: если тестирование сложное и касается не UI-части, а бэкенда, проще писать приемочные критерии в обычном формате контрольного списка.
Собственно тестирование происходит, когда задача попадает в раздел «In Test». У нас есть главная ветка, где лежат все свежие изменения, внесенные разработчиками. Когда разработчики начинают работу, они создают от этой ветки свою, которую мы обозначали таким же номером, что и соответствующая пользовательская история. После разработки эта же ветка попадает к нам, тестировщикам, и мы начинаем ее тестировать — с теми изменениями, которые были в главной ветке, и с нововведениями, сделанными разработчиками.
Какие виды тестирования мы использовали? В основном это исследовательское (exploratory) и регрессионное тестирование. Исследовательское тестирование основывается на опыте. Когда нововведения не очень большие, оно работает очень хорошо, так как это удобно и быстро — мы просто пробегаемся глазами и понимаем, что нужно найти и сделать. Ну и, конечно же, мы применяли регрессионное тестирование — когда вводится новый функционал, очень часто что-то ломается, и это важно заметить на этапе тестирования, а не потом.
К каждой тестовой задаче, как я уже говорил, нужно было добавлять доказательства того, что тест прошел хорошо. Так, в случае сложных тестовых случаев, нужно написать вводные данные и результаты.
Если при тестировании ветки мы находим ошибку, связанную именно с нововведением, мы не заводим на доске новую задачу, а просто сдвигаем эту задачу по доске назад, в раздел «In Dev» — тогда разработчики приступают к исправлению ошибки, а после исправления снова сдвигают задачу в раздел тестирования. Мы снова прогоняем регрессионные тесты и, если все в порядке, переводим задачу в раздел «Test Review». Если же мы во время тестирования случайно находим ошибку, которая не относится к нововведению и находится в другом модуле приложения, заводим новую задачу на доске, относящуюся к этой ошибке, и потом ее исправляем либо в этом, либо в следующем спринте — в зависимости от приоритетов.
Какие тесты мы пишем? Мы составляли контрольные списки для дымового тестирования и где-то раз в неделю вручную прогоняли тесты по этим спискам. А на стороне клиента у нас крутятся приемочные UI-тесты, которые прогоняются перед каждым концом спринта.
Что касается модульного и приемочного бэкенд-тестирования, настроена непрерывная интеграция (мы используем Jenkins), поэтому при сборке запускаются модульные и приемочные тесты на бэкенде — их пишут не тестировщики, а только разработчики.
Также мы, тестировщики, предлагаем свои идеи. Я считаю, каждый тестировщик должен заниматься не только тестированием, но и вносить свою лепту в создание продукта. Для этого он может посмотреть на продукт со стороны пользователя и подумать, что можно улучшить. Также тестировщик может подумать, что можно усовершенствовать в методологии разработки, как улучшить взаимодействие внутри команды. Ведь разработчики этим обычно не занимаются, а менеджера проекта в Scrum, как правило, нет.
Проблемы, которые у нас возникали
Несмотря на отлаженную схему работы, зачастую в процессе происходили сбои из-за вмешательства клиента.
Допустим, мы спланировали спринт, начинаем спокойно работать, и тут к нам приходит клиент, который говорит, что ему срочно нужно ввести такую-то функцию. Тогда приходится впихивать в спринт новую большую задачу и выводить из спринта какую-нибудь другую задачу, чтобы не раздувать его. Такое стало происходить все чаще и чаще. В итоге дошло до того, что клиент приходил, когда разработчики уже до половины выполнили какую-то задачу, но клиент хотел, чтобы они бросили работу над ней и начали работать над другой срочной задачей.
Порой задания, которые поступали от клиента посреди спринта, были огромные, а некоторые важные задачи из спринта мы выбросить не могли — тогда спринт начинал непомерно раздуваться. А самое неприятное, когда мы уже точно определили, что нужно сделать, написали отличные приемочные критерии и тесты, но тут клиент вдруг решал, что все нужно сделать совсем по-другому.
Все это сильно портило процесс. Из-за таких происшествий мы иногда не укладывались в отведенное время, а спринты раздувались — например, бывало так, что в начале спринта у нас было запланировано 25 задач, а к концу спринта оставалось 20 невыполненных, так как во время спринта количество задач доходило до 40.
Для борьбы с такой ситуацией можно попробовать, во-первых, уделять больше внимания kick-off; во-вторых, можно перед самым началом процесса разработки просить у клиента подтверждения сформулированных приемочных критериев. Однако это — временные меры. Чтобы справиться со всеми проблемами, мы решили отойти от Scrum и перейти к другой методологии — к Канбану.
Переход к канбану
В целом, принципы Канбана очень похожи на Scrum — прозрачность процесса, работа с доской, равенство в команде. Вот к чему мы в итоге пришли:
— WIP-лимит — work in progress limit. В нашем случае это ограничение максимального количества задач в одной колонке на доске.
— Меньше церемоний/. В скраме мы тратили много времени на груминг, на планирование, на ретроспективу и т. д. Мы от всего этого отказались и оставили только kick-off и ежедневные слеты — потому что без этого все равно никуда.
— Никаких итераций. Мы решили просто брать самые приоритетные задачи и спокойно над ними работать.
Также мы сократили количество столбцов на доске:
ToDo | DEV max 3 | TEST max 2 | ACCEPT max 4 | Done |
Ticket 6 | Ticket 3 | Ticket 2 | Ticket 1 | |
Ticket 5 | Ticket 7 | |||
Ticket 4 |
При этом у нас остались следующие статус-фильтры: «In Dev(2)», «Dev Review», «Ready To Test», «In Test», «In Test Review(1)», «Ready To Merge». Каждые два статуса относятся к одной колонке: «In Dev» и «Dev Review» относятся к колонке «Dev». Если у задачи статус «Ready To Test» или «In Test» — она висит в колонке «Test». «In Test Review(1)» и «Ready To Merge» висит в колонке «Accept».
Также в таблице вы можете видеть максимальное количество задач в одной колонке. Максимальное количество задач определяется скоростью, с которой работает команда. В разные периоды производительность команды и, соответственно, максимальное число одновременных задач может меняться.
Например, у нас в команде — два разработчика и тестировщик. Тогда наша доска может выглядеть вот так:
ToDo | DEV max 3 | TEST max 2 | ACCEPT max 4 | Done |
Ticket 6 | Ticket 3 | Ticket 11 | Ticket 2 | Ticket 1 |
Ticket 5 | Ticket 7 | Ticket 12 | ||
Ticket 4 | Ticket 8 |
Допустим, один разработчик работает над задачей 3, а второй — над задачей 7. У тестировщика одна задача висит с пометкой «In Test», а вторая — «Ready to Test». Тут один разработчик заканчивает разработку задачи 3, переводит ее в колонку «Ready to Test» и тут же берет себе в разработку задачу 8. Третья задача в разделе «Test» тогда выделяется красным, т. к. лимит превышен. Тогда задача отправляется разработчику обратно. Это знак, что нужно или подождать, или, что еще лучше, помочь в тестировании, чтобы разгрузить колонку «Test». Конечно, такой ситуации лучше не допускать — на самом деле, это всего лишь вопрос правильной расстановки лимитов. Если правильно расставить лимиты, у нас получается процесс в виде ровной трубы без всяких узких мест, где работа стопорилась бы, и ситуация, когда лимит превышен, случается не так уж и часто.
Мы работаем по Канбану уже несколько месяцев, и очень довольны результатами. Когда приходит клиент с новым заданием, мы ему указываем, что колонка загружена, но, как только разгрузится, приступим к работе над его заданием. В итоге проблем стало меньше, а релизы у нас получаются отличные.
Виртуальные среды
Для управления виртуальными средами мы использовали Vagrant и Docker. Vagrant —очень гибкая система поднятия и конфигурирования виртуальных машин.
Что касается Docker, это инструмент для работы с виртуальными машинами, который у нас использовался для поднятия приложений. При работе с Docker приложение собирается в разные контейнеры — в отдельные контейнеры упаковываются бэкенд, фронтенд, база данных и т. д. В контейнеры могут упаковываться любые программные части со всеми своими зависимостями. Мы поднимаем командами Docker контейнеры из нужной ветки и можем приступать к тестированию. В основном Docker используют тестировщики и фронтенд-разработчики, для разворачивания бэкенд окружения докер не обязателен.
Казалось бы, с такими инструментами организовать среду для тестирования очень просто — достаточно поднять Vagrant, поднять Docker, поднять VPN-соединение, и все. Но на самом деле все оказалось не так просто — поначалу мы тратили по неделе на настройку среды.
Так, когда мы устанавливали Vagrant, оказалось, что установка на разных ОС ведет себя очень по-разному. Например, в Windows Vagrant не всегда прописывал пути в переменных средах. Кроме того, как оказалось, Vagrant сильно завязан на конкретные версии VirtualBox — в каких-то случаях пришлось обновляться, а в каких-то — откатываться до определенной версии VirtualBox, чтобы все хорошо работало вместе.
В итоге установка может занять довольно много времени. Еще одна проблема Vagrant — большое потребление ресурсов (впрочем, это обычная проблема виртуальных машин). Особенно много Vagrant требовал оперативной памяти. Если у вас HDD, а не SSD, работать будет очень трудно.
Когда мы пытались развернуть контейнеры из нужной нам ветки, они тянулись из Jenkins, который находится в Англии. Соединение было по VPN, который очень тормозил, поэтому приложение поднималось очень долго, на один контейнер уходило 20 — 30 мин. Для решения этой проблемы мы просто попросили денег и купили железо, на которое поставили свой Jenkins и установили быстрое VPN-соединение. Теперь у нас все собиралось и разворачивалось очень быстро.
Заключение
Хотя сейчас в моде Scrum, не следует забывать, что есть еще и другие отличные методологии, например, XP или Канбан, который нам очень пригодился. Более того, на мой взгляд, иногда лучше не следовать точно какой-то определенной методологии, а пробовать их смешивать. Бывают проекты, когда ты понимаешь, что ни Scrum, ни Канбан не подходят. Тогда можно попробовать взять что-то из каждой из этих методологий.
Но, как бы то ни было, основные принципы гибкой разработки (прозрачность, общение и командный принцип) значительно облегчают жизнь. Я считаю, что без них нормально разрабатывать что-то просто невозможно. Особенно важно общаться с клиентами, чтобы ясно понимать, что именно они хотят видеть в продукте сегодня.
Если говорить о роли тестировщика в гибкой методологии, повторю — на нем лежит большая ответственность. Тестировщик составляет требования, иногда даже пишет пользовательские истории, занимается тестами, общается с клиентами, выдвигает новые идеи — другими словами, выполняет много разной и очень важной работы.
Что касается всяких модных штук вроде Vagrant и Docker, работать с ними не так просто, как хотелось бы — иногда для их настройки и установки приходится приложить немало усилий.
И наконец, чтобы облегчить себе работу, важно учиться, учиться и еще раз учиться, ведь постоянно появляются новые инструменты, новые виды тестирования и новые методологии. Сталкиваясь с проблемами, всегда можем попробовать новый подход.