Между холиваром и оверинжинирингом: что, если разработчик не доверяет тестам тестировщика

Вы — разработчик и хотя бы раз говорили тестировщику «Докажи руками»? Или вы — тестировщик и хотя бы раз слышали такое от коллег-разрабов? Либо вы — продакт или тимлид, в команде которого случались или могут случиться такие конфликты? Тогда эта статья для вас!

Кто-то после «А докажи, что это все действительно работает» или «А как ты проверял?» звереет и начинает открыто ругаться с коллегами — что ж, устраивать холивары, конечно, интересно, но бесполезно. Кто-то действительно начинает тратить ресурсы на воспроизведение бага. Однако можно выстроить такой процесс коммуникации, в котором разработчик доверяет результатам команды тестирования и даже иногда сам дополняет тесты, при этом не скатываясь в оверинжиниринг.

8aogj7sls6htjfs6tf3ws0eynv0.png

Меня зовут Илья Колесов, я — Senior SDET (Software Development Engineer in Test) в команде KasperskyOS Automotive & Embedded Quality Control «Лаборатории Касперского» и занимаюсь разработкой автоматизированных тестов на стыке embedded- и desktop-систем. В этой сфере я прошел весь путь с нуля до готовых решений. И в этой статье расскажу о взаимодействии с разработкой через автоматизацию тестирования — поделюсь своим опытом того, как удается преодолеть недоверие и сделать коммуникации более эффективными.

Почему это важно


Для начала немного контекста.

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

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

x81184w7kqi_rz1wja00ytkhyoc.png

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

Пример из жизни и аналогия


Начнем с недавнего примера.

У нас на конвейере вдруг сломался приемочный тест, который осуществлял простую проверку того, что сообщения конвертируются из одного протокола в другой (mqtt→can). Казалось бы, у нас все есть, чтобы завести хороший баг: результаты теста из CI, логи, описание сценария и тестовый прогон. Но есть и небольшой нюанс — тесты с похожими сценариями проходят.

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

Выполняем тест локально — ничего не изменилось, тест не проходит. Добавляем новые логи и уточняем описание, после чего возвращаем разработке… Но у них опять не воспроизводится.

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

Для меня это уже триггер: что-то идет не так. Видео с воспроизведением дефекта классно записывать, если тестируется то, что видно, — GUI, когда важно показать, что кнопка стала красной или сменила состояние недостаточно быстро. А когда в продукте не ходят сообщения, видео особо не поможет.

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

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

Лирическое отступление. Авторалли — как тестирование продукта, и почему я люблю эту аналогию
В свободное от работы время я пилот раллийной команды. И недавно мне пришла ассоциация: ралли очень похожи на тестирование продукта.

Продукт — это машина, которая куда-то едет.

asdon44kck_sedb9_drw8atrlf4.png

Знанием особенностей трассы обладает только штурман, пилот ведет машину, опираясь на них. Можно ехать и без подсказок, но тогда время прохождения трассы будет неконкурентоспособным, быстро ехать, опираясь только «на глаза», — страшно.

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

Какие цели мы преследуем в ралли (и в продукте)? Как минимум — добраться до финиша. Это всегда цель номер один. Если ты где-то сломался, и хотя до этого ехал очень быстро, но до финиша не доехал, результат — сход и последнее место. На следующем месте — задача добраться до финиша первым. Но есть и третья цель — добраться до финиша первым и без поломок, чтобы не тратить потом время на восстановление.

Наш продукт — автомобильный шлюз безопасности Kaspersky Automotive Secure Gateway. Помимо ограничений по скорости мы зажаты рядом стандартов и нужно четко следовать траектории — трассе, которую мы нарисовали. Любое отклонение может привести к тому, что мы не доедем до финиша (или не сможем сделать это без поломок).

7agggbbo5prbsyjaxwba-e5yzsw.png
Все фото — авторские

Тестовые сценарии


В ралли мы сначала передвигаемся по трассе медленно, вдумчиво, размышляем, как здесь проехать, и прописываем траекторию. Диктуем повороты, записываем в специальную книжечку. Переоценили допустимую скорость? Недооценили сложность поворота или рельеф покрытия — мы оказываемся в кювете. В целом ничего страшного — приезжают товарищи, достают из кювета. Но неприятно, потому что мы теряем время.

Как не терять это время? Поговорим об автоматизации.

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

d-bexesk4w--y6e8khntyqhhc1w.png

mica61furo3gfz1d6tjyb4cmsg8.png

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

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

fymwyh1kfsi5jlng0_fqwlbnm-k.png

Конечно сможем! И мы расходимся по своим командам…

fyvie8bppto2pfsi5nmemz1ev4y.png

Ребята делают функционал, мы пишем и расширяем сценарии (много блоков и связей — много сценариев). Созваниваемся с разработчиками и еще раз обсуждаем вопрос автоматизации:

bd0dc8rhi--jlaxh5lvdm2udi00.png

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

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

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

beiacuwvflmz7yzjkdqpssmkg7k.png

Вопрос поставлен по-другому: не «да или нет», а «какие инструменты мы можем использовать, чтобы это сделать». В нашем случае это был объект внутри кода продукта, который позволяет взаимодействовать с базой данных. Это может быть любой другой инструмент, позволяющий работать с API, — вариантов миллион.

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

  • инструменты;
  • API;
  • детали реализации;
  • ревью кейсов.


С таким подходом вы:

  • показываете кейсы разработчикам еще до того, как начнете их автоматизировать. Разработчики могут глубже заглянуть в кейсы, будут понимать, что и как вы делаете;
  • если разработчики дают какие-то инструменты, они уверены, что вы с их БД работаете штатными средствами. Будут понимать инструменты, которые вы используете, и через это будут доверять.


Написание кода


adigj9slovefuev2uk1dlyojzvo.png
Дисклеймер: в этой аварии пострадали только гордость и самолюбие пилота, потому что машина предназначена для таких приключений. Ну и на самом деле продукт также рассчитан на то, что иногда что-то идет не так.

Бывает так, что мы очень быстро поехали, поверив в себя и свои тесты, но где-то что-то пропустили — прочитали не тот поворот, повернули вместо направо налево — и, условно говоря, приехали в березу. Вышли, посмотрели, почесали голову. Как это ни печально, в этот раз до финиша не доехали, потеряли сколько-то времени, а потом еще больше на восстановление системы.

Что делать, когда выкатывают изменение, которое ломает тесты, и его надо быстро поддержать? Как при этом не приезжать в деревья?

Еще один пример из жизни.

Нам добавили в авторизацию пользователя сертификаты. Все почему-то это очень любят. Для тестов это всегда больно, потому что это дополнительное действие, которое влияет на каждый тест. Боль была и в этот раз: изменение поддержали, но локально у меня 9 из 10 прогонов проходили, а на CI — наоборот, 9 из 10 падали.

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

На такие баги рискуем получить в ответ сообщения, похожие на то, что мне сейчас шлют из автосервиса:

ehoysv6cp2tqdiwpd7rbthzdaxq.png

Цифры замазали, чтобы никого не травмировать.

Но с этим можно работать по-другому.

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

Очень полезно делать внутри команды pull-реквесты и ревью, чтобы все в целом понимали, как правильно и хорошо писать тесты. Иногда какие-то базовые ошибки проще отлавливать на этих этапах.

И еще один момент — за код не должно быть стыдно. Рано или поздно к вам придет разработка и скажет: «Показывай код!» Это может быть в двух случаях: когда вы нашли дефект, который не воспроизводится руками, или когда вы сами просите помощи.

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

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

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

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

#in case the client didn't connected properly to TLS we need to make full reconnect
while not self._client.connected_flag and tries < max_retries_num:
     logging.info("Broker hasn't started yet")
     self._client.disconnect()
     self._client.loop_stop()
     self._client.connect_async(self.broker, self.port)
     self._client.loop_start()
     for x in range(10):
          #additional wait for flag appearance with 100ms sleeps
          if self._client.connected_flag:
               break
          self._client.reconnect()
          time.sleep(0.1)
     tries += 1


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

ddvxut1eh7la3bqklemywi9ykoc.png

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

В целом «Нормально делай — нормально будет» — это не всегда просто. Тесты должны быть хорошо параметризованные и читаемые, внутри должны быть понятны все функции, а код должен соответствовать стандартам команды. Поддерживать такие условия непросто, и иногда в этом нет необходимости. Но во всех сложных дефектах это отрабатывает. И поддерживать хорошо написанный код проще — на расширение какого-то теста в будущем мы потратим меньше времени, поскольку будем лучше ориентироваться в написанном и в код заложена такая возможность.

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

  • сложные в реализации функции;
  • мигающие тесты;
  • как устроена инфраструктура.


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

Запуск тестов


l6japk8uyfz00yhn95r8kahrc3y.png

Финальный этап — запуск тестов.

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

nr379nwjasr7dz0ajw_kg7cr5oi.png

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

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

С одной стороны, мы стремимся запускать все в CI и результаты смотрим оттуда же (казалось бы, локальные запуски не нужны). С другой стороны, иногда нужно запустить продукт именно локально, чтобы в нем подключиться отладчиком и посмотреть, что делает тест. Именно в этот момент — самый сложный — разработчик придет и скажет: «Либо ты у себя настраивай среду разработки, либо настраивай мне свой тест». Такое в моем опыте было не раз. И инструкции не спасают, потому что их надо поддерживать. Если из условных 13 пунктов два устарело, один пропущен, то ничего не заработает.

И еще один пример из практики. Попробуйте найти одно отличие:

\\storage\address\projectName\folder1\v8.8.8.89\folder 2
\\storage\address\projectName\folder1\v8.9.8.89\folder 2


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

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

Чтобы вашим запускам верили разработчики, слушайте. На вас выливается поток вопросов:

  • Не ясно, что тестирует.
  • Не понятно, почему упал.
  • То проходит, то нет.
  • Код теста не читаем.
  • Долго выполняется.
  • Требует сложной ручной подготовки среды.


Не стоит на них остро реагировать.

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

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

Выводы


loknm9mmlfghukcbxv61kp6k7dg.png

Выводы довольно простые.

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


Это путешествие, которое начинается с любого дня вашей работы:

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


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

Если вдруг вы не согласны — добро пожаловать в комментарии :)

Ну, а если согласны — вы очень крутой SDET, и возможно, именно вас мы ждем в команде «Лаборатории Касперского».

© Habrahabr.ru