Русский
Русский
English
Статистика
Реклама

Тестирование веб-сервисов

Из песочницы Burp Suite Tips

12.07.2020 16:04:26 | Автор: admin

Burp Suite это платформа для выполнения тестирования по безопасности веб-приложений. В этой заметке я поделюсь несколькими приёмами, как использовать данный инструмент более эффективно.



Настройки


Для правильной работы с любым инструментом важно его настроить под себя. В Burp Suite существуют 2 типа настроек:


  • User Options Настройки относящиеся к самому Burp Suite
  • Project Options Настройки к тому, что хакаешь

Кодировки


При исследовании русскоязычных ресурсов, часто в ответе от сервера вместо кириллицы могут отображаться кракозябры. Чтобы этого избежать можно установить кодировку UtF-8 и продолжать работу в нормальном режиме. Настройки кодировок находятся в User Options -> Display -> Characters Sets.



Хоткеи


Чтобы действительно ускорить свою работу с Burp Suite стоит попробовать перейти на использование сочетаний клавиш. Можно использовать установленные по умолчанию, но также есть возможность перенастроить "под себя". Для управления хоткеями достаточно перейти в User Options -> Misc -> Hotkeys. Несколько полезных сочетаний:


  • Кодирование\декодирование:
    • Ctrl+(Shift)+U|H|B для URL|HTML|Base64 (de)code
  • Навигация по GUI:
    • Ctrl+Shift+T|P|S|I|R Переключение между утилитами
    • Ctrl+I|R|D "Отправить запрос в утилиту"
  • Burp Repeater:
    • Ctrl+G "Выполнить запрос в Burp Repeater"

Proxy Interception


Бывало ли у вас такое, что настроив перенаправление траффика в Burp Proxy приложение почему-то не работало, а в истории запросы так и не появлялись? У меня было так практически каждый раз при старте нового проекта. Всё из-за того, что я забывал отключать перехват в прокси. Каждый раз Чтобы не травмировать свою нервную систему, можно отключить по умолчанию эту полезную функцию. Перейдите в User -> Misc -> Proxy Interseption и выберите опцию "Always Disable".



Приватность


Не смотря на доверие разработчикам из PortSwigger не стоит передавать лишнюю информацию на их сервера. Даже если вы и сами не против "поделиться", то у ваших заказчиков могут быть более строгие правила. Первое, что необходимо сделать отключить отправку анонимных сообщений, отправляемых PortSwigger. Перейдите по пути User Options -> Misc -> Performance Feedback и запретите отправку.


Если вы используете Burp Collaborator, то стоит поднять свой собственный и использовать его. Такое решение позволит обходить некоторые WAF, настроенных на блокировку burpcollaborator.net и его поддоменов. Для управления Burp Collaborator переходим в Project Options -> Misc -> Burp Collaborator Server



Используйте конфиг по умолчанию


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


  • Сделайте необходимые изменения
  • Сохраните настройки проекта и пользователя (это будут JSON файлы).
  • Объедините оба файла в один.
  • Обновляйте его по мере необходимости и храните в надежном месте (например, git репозитории).
  • Финальный конфиг будет выглядеть так:

{    "project_options":{        // options    },    "user_options":{        // options    }}

Отключение плагинов


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



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


Ограничение по памяти


Burp Suite написан на Java, что часто приводит к большому потреблению памяти, особенно в случае долгих автоматических проверок. Для ограничения потребляемых ресурсов можно воспользоваться следующей командой:


java -jar -Xmx2048M burp.jar

Утилиты


В данном разделе будут советы по работе со встроенными утилитами в Burp Suite. Наиболее интересные из них:


  • Burp Proxy лежит в самом сердце управляемого пользователем рабочего процесса Burp Suite, позволяя вам перехватывать, проверять и модифицировать трафик, движущийся в обоих направлениях между сервером и клиентом.
  • Burp Repeater инструмент для обработки HTTP-запросов, их редактирования и анализа ответов веб-приложений вручную.
  • Burp Intruder мощный инструмент для автоматизации специализированных атак против веб-приложений. Это очень гибкий и хорошо настраиваемый инструмент, который может использоваться для выполнения огромного спектра задач, возникающих во время тестирования приложений.

Найти ссылки на хост


Иногда, появляется необходимость найти ссылки на определённый хост. Конечно, можно воспользоваться поиском в истории запросов, но есть более быстрый и эффективный способ. Переходим во вкадку Target -> Site Map, выбираем из списка необходимый хост, правый клик и Engagement tools -> Find reference. В результате, появится список запросов, ссылающихся на интересующий нас хост.



Автозамена


Многие недооценивают полезность функции автозамены в Burp Proxy. Часто её используют для подмены ответов сервера, с целью отключить защитные механизмы в заголовках; замены false на true для повышения привилегированного доступа и т.п. Для меня же наибольшая польза от этого механизма достигается при тестировании мобильных приложений. Очень удобно в самом интерфейсе приложения вводить простые слова, а в результате отправлять сложные выражения. Например, ввести bxss, а отправить полноценный пейлоад BlindXSS. Также, упрощается работа при вводе паролей. Настройки автозамены вы можете найти в Proxy -> Options -> Match and replace.



Переименование вкладок


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



Работает это не только в Burp Repeater, но и в Burp Intruder, что также бывает полезным.


Автопрокрутка


Очень удобной является функция автопрокрутки при поиске в запросах или ответах. Burp будет автоматически перепрыгивать на результат, после отправки запроса, ускоряя вашу работу. Для включения опции после ввода в поисковой строке того, что вы хотите найти, нажмите кнопку "+", чтобы получить доступ к опциям поиска и отметье "Auto-scroll to match when text changes".



Отчёты


При оформлении отчётов гораздо больше информации поместится и симпатичней будут смотреться скриншоты с вертикальным расположением запроса\ответа. Для этого в опциях Burp Repeater отметьте View->Top/bottom split



Целевое сканирование


Вы можете использовать интерфейс Burp Intruder, чтбы сконфигурировать сканирование с использованием Burp Scanner только на необходимые параметры, заголовки и т.п. Для этого, выставите маркеры в нужном вам запросе в интерфейсе Burp Intruder так, как вы это делаете обычно, а затем выберите "Scan defined insertion points" из контекстного меню. В результате будет сэкономлено достаточно времени, т.к. по умолчанию Burp Scanner тестирует все, что доступно в запросе, включая cookies, заголовки, URI, параметры запроса.



Обработка полезной нагрузки на лету


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


Примеры доступных типов правил:


  • Add prefix / suffix Добавляет текст до или после нагрузки.
  • Match / replace Заменяет любую часть в нагрузке, подходящую под регулярное выражение, указанной строкой.
  • Encode / Decode Кодирует или раскодирует нагрузку различных типов: URL, HTML, Base64, ASCII hex.
  • Hash Производит операцию хэширования над нагрузкой.
  • Skip if matches regex Проверяет удовлетворяет ли нагрузка указанному регулярному выражению и если это так, то пропускает нагрузку и переходит к следующей. Это может быть полезным, например, если вам известно, что значение параметра должно иметь минимальную длину и вы хотите пропустить все значения из списка, которые короче данного значения.


Сортировка результатов Intruder


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



Использование этой опции становится максимально полезным при анализе больших объемов результатов сканирований и позволяет быстро находить интересующие вас вещи. Например, при тестировании SQL инъекций поиск сообщений содержащих "ODBC", "error" и пр. помогут быстро отыскать уязвимые параметры.

Подробнее..

Перевод Регрессионная спираль смерти

27.07.2020 20:17:32 | Автор: admin

Перевод статьи подготовлен в преддверии старта курса Автоматизация тестирования на JavaScript




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


Вы работаете QA в небольшой команде разработчиков; сегодня утро четверга второй недели вашего двухнедельного спринта. У вашей команды еще несколько историй (story) в разработке, которые должны быть утверждены к утру пятницы, чтобы быть готовыми к демонстрации днем. Ваши планы на вечер четверга начинают таять на ваших глазах.


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


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


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


Конец истории


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


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


Регрессионное тестирование


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


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


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


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


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


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


Agile Delivery и бремя регрессии


Agile Delivery вводит две концепции, относящиеся к регрессионной спирали смерти. Во-первых, Agile Delivery делит фичи на небольшие, отдельные пользовательские истории (user stories). Каждая пользовательская история должна быть атомарной, чтобы ее можно было разрабатывать и тестировать как отдельный объект. Во-вторых, Agile Delivery способствует коротким циклам разработки с небольшими итеративными релизами. Все виды и гибриды Agile, Scrum, Kanban, SAFE, LeSS, ScrumBan и т. д. по-своему используют эти концепции. Давайте посмотрим на каждую в отдельности.


Agile Delivery отходит от использования больших функций, определенных (даже более крупными) документами спецификаций, к пользовательским историям, каждая из которых представляет собой небольшую часть общего объема работ. Истории Agile независимо разрабатываются, тестируются, ревьюятся и утверждаются.


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



История 13 была внедрена и протестирована. Нам не о чем беспокоиться?
/ НЕТ! Изменения в коде могли повлиять на другие истории (как связанные, так и не связанные с этой историей)


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


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


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


Давайте рассмотрим простой гипотетический набор спринтов, чтобы прояснить, насколько быстро стоимость регрессии может отягощать скорость команды.


Предположим, у нас есть недавно созданная Agile-команда, которая только начинает свою первую итерацию разработки (спринт). На первой итерации (или некоторой начальной продолжительности в потоковой модели) у нас может быть три истории для тестирования. Тестирование этих историй довольно простое мы просто тестируем эти три истории. Во второй итерации мы добавляем две дополнительные истории. Сколько усилий будет затрачено на тестирование этих историй? Наивный ответ несколько меньше усилий, чем для первого спринта, так как мы тестируем только две истории. Однако, основываясь на нашем понимании того, что изменения кода могут вызвать регрессию в любой области приложения, мы знаем, что мы должны протестировать обе новые истории, а также регрессировать три ранее завершенные истории.



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


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


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



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


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



Автоматизация в Agile Delivery


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


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


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


Если учесть моменты двух предыдущих разделов то, что изменения в программном обеспечении могут иметь непреднамеренные последствия, и что agile delivery это просто непрерывный поток небольших изменений становится очевидным, что agile delivery не может функционировать без автоматизации тестирования. Попытка итеративной доставки без него это все равно, что отправиться в дальнюю дорогу, но отказаться от остановок, потому что остановки замедляют вас. Топливо фундаментальная часть автомобильного путешествия (на данный момент), так же, как автоматизация тестирования фундаментальная часть Agile Delivery. Автоматизация это единственное, что может смягчить удушающее бремя регрессии и избежать резкого падения скорости разработки.



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


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


Я твердо убежден в том, что без полной автоматизации тестирования не может быть здоровой Agile Delivery. Полная автоматизация историй с набором средств автоматизации, охватывающим все виды тестов (модуль, компонент, интеграция, пользовательский интерфейс и т. д.), абсолютно необходима для поддержания скорости доставки на протяжении всего жизненного цикла. Любое предложение о послаблении этого требования для каждой истории следует отвергать и объяснять, что это такое краткосрочное отвлечение для создания восприятия успеха, в то же время ставящее под угрозу долгосрочные цели развития. Не говоря уже о здравом уме.


Спираль смерти


Вернемся к нашей маленькой истории.


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


Вы собираетесь протестовать, когда ваш скрам-мастер вмешивается: Мы не можем этого сделать! Это первый шаг к регрессионной спирали смерти!


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


Вы хотите, чтобы мы преуспели или просто казались преуспевающими? Эти слова вашего бизнес-аналитика, вызывает улыбку на вашем лице.


Вы решаете отменить собеседование на следующей неделе.


Слова благодарности


Я не изобрел термин регрессионная спираль смерти, но никогда не видел его в письменной форме. Самое раннее использование термина, которое я могу найти, находится в Тестировании экстремального программирования (2002) Лизы Криспин и Тип Хаус.


Несколько других ссылок можно найти с помощью быстрого поиска в Google:


http://ivory.idyll.org/blog/software-quality-death-spiral.html (2008)


https://xebia.com/blog/regression-testing-with-an-agile-mindset (2010)




Узнать всё о курсе Автоматизация тестирования на JavaScript



Подробнее..

Из песочницы Модульное и интеграционное тестирование в Redux Saga на примерах

29.07.2020 18:17:33 | Автор: admin

hero image


Redux чрезвычайно полезная библиотека, которая облегчает управление состоянием приложения. Среди многих дополнений, Redux-Saga подходит мне лучше всего. В проекте на React-Native, над которым я сейчас работаю, мне приходилось сталкиваться с множеством побочных эффектов. Они приносили бы мне головные боли в случае, если я поместил их в компоненты. С помощью этого инструмента создание сложных логических потоков с разветвлениями становится простой задачей. Но как насчет тестирования? Так же это просто, как и использование библиотеки? Хотя я не могу дать вам точный ответ, я покажу вам реальный пример проблем, с которыми я столкнулся.


Если вы не знакомы с тестированием саг, я рекомендую прочитать отдельную страницу в документации. В следующих примерах я использую redux-saga-test-plan, поскольку эта библиотека дает полную силу интеграционного тестирования наряду с модульным тестированием.


Немного о модульном тестировании


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


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

// Только самые важные импортыimport {call, put, take} from "redux-saga/effects";export function* initApp() {    // Инициализация хранилища и     // получение последней сохраненной сессии    yield put(initializeStorage());    yield take(STORAGE_SYNC.STORAGE_INITIALIZED);    yield put(loadSession());    let { session } = yield take(STORAGE_SYNC.STORAGE_SESSION_LOADED);    // Загрузка последнего проекта    if (session) {        yield call(loadProject, { projectId: session.lastLoadedProjectId });    } else {        logger.info({message: "No session available"});    }}

// Только самые важные импортыimport {testSaga} from "redux-saga-test-plan";it("должно загрузить последнюю сессию и вызвать `loadProject`", () => {    const projectId = 1;    const mockSession = {        lastLoadedProjectId: projectId    };    testSaga(initApp)        // `next` служит командой перехода на следующую конструкцию `yield`        // при этом мы можем передать аргумент,        // который будет выходным результатом действующего `yield`        // Фактически он просто запускает новую итерацию         //(не забываем что функции-генераторы возвращают итератор)        .next()        .put(initializeStorage())        .next()        .take(STORAGE_SYNC.STORAGE_INITIALIZED)        .next()        .put(loadSession())        .next()        .take(STORAGE_SYNC.STORAGE_SESSION_LOADED)        // Сохранение метки, чтобы иметь возможность вернуться        .save("до развилки")        // Передаем объект, который будет возвращен из `yield take...`        .next({session: mockSession})        .call(loadProject, {projectId})        .next()        .isDone()        // Возвращаемся назад к метке        .restore("до развилки")        // Проверяем, что функция сразу завершится,        // если последняя сессия будет отсутствовать        .next({})        .isDone();});

Из примера вы можете увидеть обычный способ проверки генераторов эффектов. Если бы требовалось заменить вызовы каких-то API, я бы сделал это, используя jest.fn.


Так как мы завершили, давайте перейдем к основному блюду!


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


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


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


Давайте рассмотрим следующий пример, который является адаптированной версией кода из моего проекта:


// Только самые важные импортыimport {call, fork, put, take, takeLatest, select} from "redux-saga/effects";// Корневая сагаexport default function* sessionWatcher() {    yield fork(initApp);    yield takeLatest(SESSION_SYNC.SESSION_LOAD_PROJECT, loadProject);}export function* initApp() {    // Инициализация хранилища и получение последней сохраненной сессии    yield put(initializeStorage());    yield take(STORAGE_SYNC.STORAGE_INITIALIZED);    yield put(loadSession());    let { session } = yield take(STORAGE_SYNC.STORAGE_SESSION_LOADED);    // Загрузка последнего проекта    if (session) {        yield call(loadProject, { projectId: session.lastLoadedProjectId });    } else {        logger.info({message: "Нет доступной сессии"});    }}export function* loadProject({ projectId }) {    // Загрузка последнего проекта и последующая попытка его обработки    yield put(loadProjectIntoStorage(projectId));    const project = yield select(getProjectFromStorage);    // Сохранение проекта, сессии с ново загрузившимся проектом и загрузка карты    try {        yield put({type: SESSION_ASYNC.SESSION_PROJECT_LOADED_ASYNC, project});        yield fork(saveSession, projectId);        yield put(loadMap());    } catch(error) {        yield put({type: SESSION_SYNC.SESSION_ERROR_WHILE_LOADING_PROJECT, error});    }}export function getProjectFromStorage(state) {    // Вытягивает загруженный проект из состояния системы}export function* saveSession(projectId) {    // .... Вызывает внешние API    yield call(console.log, "Вызов API...");}

Здесь у нас есть корневая сага sessionWatcher, которая инициализирует приложение, вызывая initApp сразу после загрузки, а также ожидает команды по загрузке проекта по id. Проект загружается из хранилища, после чего мы сохраняем проект в состоянии системы и вызываем другую сагу, которая сохраняет сеанс и загружает карту. В примере показаны всевозможные проблемы, с которыми мы можем столкнуться в процессе тестирования:


  • Работа с несколькими сагами
  • Доступ к состоянию
  • Вызовы API, которых мы хотели бы избежать.

// Только самые важные импортыimport { expectSaga } from "redux-saga-test-plan";import { select } from "redux-saga/effects";import * as matchers from "redux-saga-test-plan/matchers";it("должно инициализировать приложение и загрузить последний проект из предыдущей сессии", () => {    // Стадия подготовки    const projectId = 1;    const anotherProjectId = 2;    const mockedSession = {        lastLoadedProjectId: projectId,    };    const mockedProject = "project";    // Тестируем `sessionWatcher`    // `silentRun` в конце возвращает промис с некоторыми плюшками для тестирования    // подробнее можете почитать в официальной документации    return (        expectSaga(sessionWatcher)            // Подмена генераторов эффектов            .provide([                // Заменить каждый генератор `select` эффекта, который использует                // `getProjectFromStorage` и на его месте вернуть `mockedProject`                // при этом обращать внимание как на вызываемую функцию так и на аргументы,                // которые мы можем передать в `select`,                // в нашем случае мы не передаем никаких                // Пример использования стандартного генератора эффектов                // из Redux-Saga, как фильтра                [select(getProjectFromStorage), mockedProject],                // Заменить каждый генератор `fork` эффекта, который вызывает `saveSession`                 // и ничего не вернуть (undefined)                // при этом обращать внимание только на вызываемую функцию,                // игнорирую аргументы                // Пример использования фильтров из Redux Saga Test Plan                [matchers.fork.fn(saveSession)],            ])            // Порядок не имеет значения            // Мы упоминаем только те генераторы эффектов, которые мы хотим проверить            // Тестирование инициализации            .put(initializeStorage())            .take(STORAGE_SYNC.STORAGE_INITIALIZED)            // Генерация команды, которую в данный момент ожидает первый `take` в `initApp`            // Генераторы команд ДОЛЖН вызываться в нужном порядке            .dispatch({ type: STORAGE_SYNC.STORAGE_INITIALIZED })            .put(loadSession())            .take(STORAGE_SYNC.STORAGE_SESSION_LOADED)            .dispatch({ type: STORAGE_SYNC.STORAGE_SESSION_LOADED, session: mockedSession })            // Тест загрузки проекта, вызванную `initApp`            .put(loadProjectFromStorage(projectId))            .put({ type: SESSION_ASYNC.SESSION_PROJECT_LOADED_ASYNC, project: mockedProject })            .fork(saveSession, projectId)            .put(loadMap())            // Генерируем команду, которая будет перехвачена `takeLatest` в `sessionWatcher`            // и опять протестировать загрузку проекта            // Тест загрузки проекта, вызванную `sessionWatcher`            .dispatch({ type: SESSION_SYNC.SESSION_LOAD_PROJECT, projectId: anotherProjectId })            .put(loadProjectFromStorage(anotherProjectId))            .put({ type: SESSION_ASYNC.SESSION_PROJECT_LOADED_ASYNC, project: mockedProject })            .fork(saveSession, anotherProjectId)            .put(loadMap())            // Запустить тест, подавить ошибку связанную с истечением времени            .silentRun()    );});

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


Первая функция, которую мы видим, provide использует фильтры, чтобы найти генераторы эффектов и впоследствии заменить их. Первый кортеж (пара значений) использует генератор эффекта select из библиотеки Redux Saga и в точности соответствует эффекту, вызывающего getProjectFromStorage. Если мы хотим большей гибкости, мы можем использовать средства сопоставления, которые предоставляются библиотекой Redux Saga Test Plan. Пример этого мы можем увидеть во втором кортеже, где мы говорим, что фильтрация будет происходить по вызываемой функции saveSession, игнорируя ее аргументы. Этот механизм позволяет нам избежать использования состояния системы, подмены вызовов нежелательных функций или API.


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


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


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


Чтобы смоделировать возникновение ошибки, мы можем использовать уже знакомую функцию provide вспомогательную от redux-saga-test-plan/providers, чтобы заменить генератор эффекта выбросом ошибки ошибкой.


// Только самые важные импортыimport {expectSaga} from "redux-saga-test-plan";import {select} from "redux-saga/effects";import * as matchers from "redux-saga-test-plan/matchers";import * as providers from "redux-saga-test-plan/providers";it("должно инициализировать приложение и обработать ошибку загрузки проекта", () => {    const projectId = 1;    const mockedSession = {        lastLoadedProjectId: projectId    };    const mockedProject = "project";    const mockedError = new Error("Оууу, кажется что-то пошло не так!");    return expectSaga(sessionWatcher)        .provide([            [select(getProjectFromStorage), mockedProject],            // Заменяем генератор эффекта ошибкой            [matchers.fork.fn(saveSession), providers.throwError(mockedError)]        ])        // Тестирование инициализации        .put(initializeStorage())        .take(STORAGE_SYNC.STORAGE_INITIALIZED)        .dispatch({type: STORAGE_SYNC.STORAGE_INITIALIZED})        .put(loadSession())        .take(STORAGE_SYNC.STORAGE_SESSION_LOADED)        .dispatch({type: STORAGE_SYNC.STORAGE_SESSION_LOADED, session: mockedSession})        // Тест загрузки проекта, вызванную `initApp`        .put(loadProjectFromStorage(projectId))        .put({type: SESSION_ASYNC.SESSION_PROJECT_LOADED_ASYNC, project: mockedProject})        // Ожидаем возникновение ошибки здесь        .fork(saveSession, projectId)        // Тестируем, что ошибка была обработана        .put({type: SESSION_SYNC.SESSION_ERROR_WHILE_LOADING_PROJECT, error: mockedError})        .silentRun();});

Использование модификаторов состояния и самого состояния


А как же глобальное состояние приложения, как нам протестировать код, используя модификаторы (reducers). С redux-saga-test-plan это становится тривиальной задачей. Во-первых, нам нужно представить модификатор состояния:


const defaultState = {    loadedProject: null,};export function sessionReducers(state = defaultState, action) {    if (!SESSION_ASYNC[action.type]) {        return state;    }    const newState = copyObject(state);    switch(action.type) {        case SESSION_ASYNC.SESSION_PROJECT_LOADED_ASYNC: {            newState.loadedProject = action.project;        }    }    return newState;}

Во-вторых, мы немного изменим наш тест, добавив withReducer, который позволяет нам использовать динамическое состояние (вы можете предоставить состояние без модификатора, вызвав withState). Также мы добавим тест hasFinalState, который сравнивает состояние с ожидаемым.


// Только самые важные импортыimport {expectSaga} from "redux-saga-test-plan";import {select} from "redux-saga/effects";import * as matchers from "redux-saga-test-plan/matchers";it("должно инициализировать приложение и загрузить последний проект из предыдущей сессии", () => {    const projectId = 1;    const mockedSession = {        lastLoadedProjectId: projectId    };    const mockedProject = "project";    const expectedState = {        loadedProject: mockedProject    };    return expectSaga(sessionWatcher)        // Вы можете подключить корневой модификатор, чтобы        // динамически изменять состояние либо указать его статически с помощью `withState`        .withReducer(sessionReducers)        .provide([            [select(getProjectFromStorage), mockedProject],            [matchers.fork.fn(saveSession)]        ])        // Тестирование инициализации        .put(initializeStorage())        .take(STORAGE_SYNC.STORAGE_INITIALIZED)        .dispatch({type: STORAGE_SYNC.STORAGE_INITIALIZED})        .put(loadSession())        .take(STORAGE_SYNC.STORAGE_SESSION_LOADED)        .dispatch({type: STORAGE_SYNC.STORAGE_SESSION_LOADED, session: mockedSession})        // Тест загрузки проекта, вызванную `initApp`        .put(loadProjectFromStorage(projectId))        // Теперь мы можем пропустить некоторые тесты, которые изменяют состояние,        // так как мы его тестируем в конце        // .put({type: SESSION_ASYNC.SESSION_PROJECT_LOADED_ASYNC, project: mockedProject})        .fork(saveSession, projectId)        .put(loadMap())        // Тестирование конечного состояния        .hasFinalState(expectedState)        .silentRun();});

Это была локализация моей статьи с Medium.


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

Подробнее..

Утреннее шоу с Lucas F. Costa JS, CS и тестирование веб-приложений

05.08.2020 14:08:45 | Автор: admin


Привет, Хабр!
Мы решили поэкспериментировать с форматом наших пижамных разговоров и сделать живой утренний выпуск. У нас в гостях Lucas F. Costa, бразильский software engineer, живущий сейчас в Лондоне.


Лукас большой фанат JavaScript, опен-сорса и мейнтейнер Chai.js and Sinon.js. Также вы можете знать его по книге Testing JavaScript Applications и выступлениям на конференции HolyJS.
Мы поговорим с Лукасом о JS, тестировании и куда же без утренней чашечки CoffeeScript. Конечно же, будем рады вашим вопросам здесь и в эфире.
Встретимся завтра, 6 августа, в 10 утра по Москве. Встреча пройдет на английском языке.

Трансляция
Добавить в Google Calendar
Другие выпуски Wrike Pyjama Talks
Подробнее..

Перевод Canary Deployment в Kubernetes 3 Istio

03.08.2020 14:12:05 | Автор: admin

Использование Istio+Kiali для запуска и визуализации Canary деплоя





Статьи этого цикла


  1. Canary Deployment в Kubernetes #1: Gitlab CI
  2. Canary Deployment в Kubernetes #2: Argo Rollouts
  3. (эта статья)
  4. Canary Deployment используя Jenkins-X Istio Flagger

Canary Deployment


Надеемся, что вы читали первую часть, где мы кратко объясняли что такое Canary deployments и показывали как его реализовать при помощи стандартных ресурсов Kubernetes.


Istio


И мы предполагаем, что читая эту статью, вы уже знаете что такое Istio. Если нет, то вы можете почитать о нем здесь.


Приложение для тестов



Каждый под содержит два контейнера: наше приложение и istio-proxy.


Мы будем использовать простое тестовое приложение с подами frontend-nginx и backend на python. Под с nginx будет просто перенаправлять каждый запрос на под с backend и работать как прокси. Детали можно посмотреть подробнее в следующих yamls:



Запуск тестового приложения самостоятельно


Если вы хотите последовать моему примеру и использовать данное тестовое приложение самостоятельно, см. readme проекта.


Начальный Deployment


При запуске первого Deployment видим, что поды нашего приложения имеют всего по 2 контейнера, то есть Istio sidecar пока только внедряется:





И также видим Istio Gateway Loadbalancer в namespace istio-system:



Создание трафика


Мы будем использовать следующий IP, что бы сгенерировать трафик, который будет принят frontend подами и перенаправлен на backend поды:


while true; do curl -s --resolve 'frontend.istio-test:80:35.242.202.152' frontend.istio-test; sleep 0.1; done


Мы так же добавим frontend.istio-test в наш hosts файл.


Просмотр Mesh через Kiali


Мы установили тестовое приложение и Istio вместе с Tracing, Grafana, Prometheus и Kiali (подробнее см. readme проекта). Следовательно, мы можем использовать Kiali через:


istioctl dashboard kiali # admin:admin



Kiali визуализирует текущий трафик через Mesh


Как мы видим, 100% трафика попадает на frontend service, затем на под фронтенда с label v1, так как мы используем простой nginx-прокси, который перенаправляет запросы на backend service, который в свою очередь перенаправляет их на backend поды с label v1.


Kiali работает отлично вместе с Istio и предоставляет коробочное решение для визуализации Mesh. Просто прекрасно.


Canary Deployment


Наш бекенд уже имеет два k8s deployments, один для v1 и один для v2. Теперь нам нужно просто сказать Istio перенаправлять определенный процент запросов на v2.


Шаг 1: 10%


И все что нам нужно сделать это отрегулировать вес VirtualService в istio.yaml:


apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:  name: backend  namespace: defaultspec:  gateways: []  hosts:  - "backend.default.svc.cluster.local"  http:  - match:    - {}    route:    - destination:        host: backend.default.svc.cluster.local        subset: v1        port:          number: 80      weight: 90    - destination:        host: backend.default.svc.cluster.local        subset: v2        port:          number: 80      weight: 10




Мы видим, что 10% запросов перенаправлено на v2.


Шаг 2: 50%


И теперь достаточно просто увеличить его до 50%:


apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:  name: backend  namespace: defaultspec:...    - destination:        host: backend.default.svc.cluster.local        subset: v1        port:          number: 80      weight: 50    - destination:        host: backend.default.svc.cluster.local        subset: v2        port:          number: 80      weight: 50




Шаг 3: 100%


Теперь Canary deployment может считаться завершенным и весь трафик перенаправляется на v2:





Тестирование Canary вручную


Допустим, сейчас мы отправляем на бэкэнд v2 10% от всех запросов. Что, если мы хотим вручную протестировать v2, чтобы убедиться, что все работает как мы ожидаем?


Мы можем добавить специальное соответствующее правило, основанное на HTTP заголовках:


apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:  name: backend  namespace: defaultspec:  gateways: []  hosts:  - "backend.default.svc.cluster.local"  http:  - match:    - headers:        canary:          exact: "canary-tester"    route:    - destination:        host: backend.default.svc.cluster.local        subset: v2        port:          number: 80      weight: 100  - match:    - {}    route:    - destination:        host: backend.default.svc.cluster.local        subset: v1        port:          number: 80      weight: 90    - destination:        host: backend.default.svc.cluster.local        subset: v2        port:          number: 80      weight: 10

Теперь используя curl мы можем принудительно запросить v2 отправив заголовок:





Запросы без заголовка все еще будут управляться соотношением 1/10:





Canary для двух зависимых версий


Теперь мы рассмотрим вариант, где у нас есть версия v2 и для frontend и для backend. Для обоих мы указали, что 10% трафика должно идти к v2:





Мы видим, что frontend v1 и v2 оба пересылают трафик в соотношении 1/10 на backend v1 и v2.


А что если бы мы нам было нужно пересылать трафик с frontend-v2 только на backend-v2, потому что он не совместим с v1? Для этого мы установим 1/10 соотношение для frontend, который контролирует какой трафик попадает на backend-v2 используя согласование по sourceLabels :


apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:  name: backend  namespace: defaultspec:  gateways: []  hosts:  - "backend.default.svc.cluster.local"  http:...  - match:    - sourceLabels:        app: frontend        version: v2    route:    - destination:        host: backend.default.svc.cluster.local        subset: v2        port:          number: 80      weight: 100

В результате получаем то, что нужно:





Отличия от ручного Canary подхода


В первой части мы выполняли Canary deployment вручную, также используя два k8s deployments. Там мы управляли соотношением запросов, изменяя количество реплик. Такой подход работает, но имеет серьезные недостатки.


Istio дает возможность определять соотношение запросов вне зависимости от количества реплик. Это значит, к примеру, что мы можем использовать HPAs (Horizontal Pod Autoscalers горизонтальное масштабирование подов) и его не нужно настраивать в соответствии с текущем состоянием Canary деплоя.


Итог


Istio работает отлично и используя его вместе с Kiali получаем очень мощную комбинацию. Следующий в моем списке интересов это комбинация Spinnaker с Istio для автоматизации и Canary-аналитики.

Подробнее..

Перевод Изучаем mutmut инструмент для мутационного тестирования на Python

27.07.2020 14:05:23 | Автор: admin
Мутационное тестирование позволяет выявить баги, которые не покрыты обычными тестами.

У вас есть тесты на все случаи жизни? Или может быть, в репозитории вашего проекта даже лежит справка О 100-процентном тестовом покрытии? Но разве в реальной жизни всё так просто и достижимо?




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

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

В Python основным инструментом мутационного тестирования является mutmut.

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

def hours_hand(hour, minutes):    base = (hour % 12 ) * (360 // 12)    correction = int((minutes / 60) * (360 // 12))    return base + correctiondef minutes_hand(hour, minutes):    return minutes * (360 // 60)def between(hour, minutes):    return abs(hours_hand(hour, minutes) - minutes_hand(hour, minutes))


Напишем обычный юнит-тест:

import angledef test_twelve():    assert angle.between(12, 00) == 0


В коде нет ни одного if. Давайте проверим, насколько покрывает все возможные ситуации такой юнит-тест:

$ coverage run `which pytest`============================= test session starts ==============================platform linux -- Python 3.8.3, pytest-5.4.3, py-1.8.2, pluggy-0.13.1rootdir: /home/moshez/src/mut-mut-testcollected 1 item                                                              tests/test_angle.py .                                                    [100%]============================== 1 passed in 0.01s ===============================


Отлично! Вроде бы 100-процентное покрытие. Но что произойдёт, когда мы проведём мутационное тестирование?



О нет! Из 21 выжили целых 16 мутантов. Как же так?

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

Ну ладно. Всё ясно. Надо писать юнит-тесты лучше. Тогда с помощью команды results посмотрим, какие конкретно изменения были сделаны:

$ mutmut results<snip>Survived :( (16)---- angle.py (16) ----4-7, 9-14, 16-21$ mutmut apply 4$ git diffdiff --git a/angle.py b/angle.pyindex b5dca41..3939353 100644--- a/angle.py+++ b/angle.py@@ -1,6 +1,6 @@ def hours_hand(hour, minutes):     hour = hour % 12-    base = hour * (360 // 12)+    base = hour / (360 // 12)     correction = int((minutes / 60) * (360 // 12))     return base + correction


Это типичный пример работы mumut: он анализирует исходный код и заменяет одни операторы на другие: например, сложение на вычитание или, как в данном случае, умножение на деление. Модульные тесты, вообще говоря, должны выявлять ошибки при смене оператора; в противном случае они не проверяют поведение программы эффективно. Этой логики mutmut как раз и придерживается, внося те или иные изменения.

Мы можем использовать команду mutmut apply (применить мутацию к нашему коду) для выжившего мутанта. Ничего себе: оказывается, мы не проверили, правильно ли использовался параметр час (hour). Исправим это:

$ git diffdiff --git a/tests/test_angle.py b/tests/test_angle.pyindex f51d43a..1a2e4df 100644--- a/tests/test_angle.py+++ b/tests/test_angle.py@@ -2,3 +2,6 @@ import angle  def test_twelve():     assert angle.between(12, 00) == 0++def test_three():+    assert angle.between(3, 00) == 90


Раньше мы проверяли только для 12. Добавление проверки для значения 3 спасёт ситуацию?



Этот новый тест сумел убить двух мутантов: это лучше, чем раньше, но нужно поработать ещё. Я не буду сейчас писать решение для каждого из 14 оставшихся случаев, потому что идея и так понятна (можете ли вы убить всех мутантов самостоятельно?)

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



На правах рекламы


VDSina предлагает виртуальные серверы на Linux и Windows выбирайте одну из предустановленных ОС, либо устанавливайте из своего образа.

Подробнее..

Перевод Canary Deployment в Kubernetes 1 Gitlab CI

29.07.2020 16:12:53 | Автор: admin

Мы будем использовать Gitlab CI и ручной GitOps для внедрения и использования Canary-деплоя в Kubernetes





Статьи из этого цикла:


  • (эта статья)
  • Canary Deployment при помощи ArgoCI
  • Canary Deployment при помощи Istio
  • Canary Deployment при помощи Jenkins-X Istio Flagger

Выполнять Canary-деплой мы будем руками через GitOps и создание/изменение основных ресурсов Kubernetes. Эта статья предназначена в первую очередь для знакомства с тем, как работает в Kubernetes Canary деплой, так как есть более эффективные способы автоматизации, которые мы рассмотрим в следующих статьях.



https://www.norberteder.com/canary-deployment/


Canary Deployment


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


Kubernetes Deployment (rolling update)


Стратегия по умолчанию для Kubernetes Deployment это rolling-update, где запускается определенное количество подов с новыми версиями образов. Если они создались без проблем, поды со старыми версиями образов завершаются, а новые поды создаются параллельно.


GitOps


Мы используем GitOps в этом примере, так как мы:


  • используем Git как единый источник истины
  • используем Git Operations для сборки и деплоя (никаких команд, кроме git tag/merge не нужно)

Пример


Возьмем хорошую практику иметь один репозиторий для кода приложений и один для инфраструктуры.


Репозиторий для приложений


Это очень простая API на Python+Flask, возвращающая ответ в виде JSON. Мы соберем пакет через GitlabCI и запушим результат в Gitlab Registry. В регистри у нас есть две разные версии релизов:


  • wuestkamp/k8s-deployment-example-app:v1
  • wuestkamp/k8s-deployment-example-app:v2

Единственная разница между ними это изменение возвращаемого JSON-файла. Мы используем это приложение для максимально простой визуализации того, с какой версией мы общаемся.


Инфраструктурный репозиторий


В этой репе мы будем деплоить через GitlabCI в Kubernetes, .gitlab-ci.yml выглядит следующим образом:


image: traherom/kustomize-dockerbefore_script:   - printenv   - kubectl versionstages: - deploydeploy test:   stage: deploy   before_script:     - echo $KUBECONFIG   script:     - kubectl get all     - kubectl apply -f i/k8s   only:     - master

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


gcloud container clusters create canary --num-nodes 3 --zone europe-west3-bgcloud compute firewall-rules create incoming-80 --allow tcp:80

Вам нужно сделать форк https://gitlab.com/wuestkamp/k8s-deployment-example-canary-infrastructure и создать переменную KUBECONFIG в GitlabCI, которая будет содержать конфиг для доступа kubectl к вашему кластеру.


О том, как получить учетные данные для кластера (Gcloud) можно почитать вот тут.


Инфраструктурный Yaml


В инфраструктурном репозитории у нас есть service:


apiVersion: v1kind: Servicemetadata: labels:   id: app name: appspec: ports: - port: 80   protocol: TCP   targetPort: 5000 selector:   id: app type: LoadBalancer

И deployment в deploy.yaml:


apiVersion: apps/v1kind: Deploymentmetadata: name: appspec: replicas: 10 selector:   matchLabels:     id: app     type: main template:   metadata:     labels:       id: app       type: main   spec:     containers:     - image: registry.gitlab.com/wuestkamp/k8s-deployment-example-app:v1       name: app       resources:         limits:           cpu: 100m           memory: 100Mi

И другой deployment в deploy-canary.yaml:


kind: Deploymentmetadata: name: app-canaryspec: replicas: 0 selector:   matchLabels:     id: app     type: canary template:   metadata:     labels:       id: app       type: canary   spec:     containers:     - image: registry.gitlab.com/wuestkamp/k8s-deployment-example-app:v2       name: app       resources:         limits:           cpu: 100m           memory: 100Mi

Заметьте, что app-deploy пока не имеет определенных реплик.


Выполнение начального деплоя


Для запуска начального deployment вы можете запустить пайплайн GitlabCI вручную в мастер-ветке. После этого kubectl дожен вывести следующее:





Мы видим app deployment c 10 репликами и app-canary с 0. Так же есть LoadBalancer с которого мы можем обращаться через curl по External IP:


while true; do curl -s 35.198.149.232 | grep label; sleep 0.1; done





Мы видим, что наше тестовое приложение возвращает только v1.


Выполнение Canary деплоя


Шаг 1: выпустить новую версию для части пользователей


Мы установили количество реплик в 1 в файле deploy-canary.yaml и образ новой версии:


kind: Deploymentmetadata: name: app-canaryspec: replicas: 1 selector:   matchLabels:     id: app     type: canary template:   metadata:     labels:       id: app       type: canary   spec:     containers:     - image: registry.gitlab.com/wuestkamp/k8s-deployment-example-app:v2       name: app       resources:         limits:           cpu: 100m           memory: 100Mi

В файле deploy.yaml мы изменили количество реплик до 9:


kind: Deploymentmetadata: name: appspec: replicas: 9 selector:   matchLabels:     id: app...

Мы пушим эти изменения в репозиторий, из которого запустится деплой (через GitlabCI) и видим в итоге:





Наш Service будет указывать на оба деплоя, так как у обоих есть селектор app. Из-за случайного распределения по умолчанию в Kubernetes мы должны увидеть разные ответы на ~ 10% запросов:





Текущее состояние нашего приложения (GitOps, взятый с Git как с Single Source Of Truth) это наличие двух deployments c активными репликами, по одному для каждой версии.


~10% пользователей знакомятся с новой версией и ненамеренно тестируют ее. Теперь настало время проверить наличие ошибок в логах и данных мониторинга для поиска проблем.


Шаг 2: выпустить новую версию для всех пользователей


Мы решили, что все прошло хорошо и теперь нам нужно развернуть новую версию на всех пользователей. Для этого мы просто обновляем deploy.yaml устанавливая новую версию образа и количество реплик, равное 10. В deploy-canary.yaml мы устанавливаем количество реплик равное обратно 0. После деплоя результат будет следующим:





Подводя итог


Для меня запуск деплоя вручную таким образом помогает понять как легко он может быть настроен при помощи k8s. Так как Kubernetes позволяет обновлять все через API, эти шаги можно автоматизировать через скрипты.


Еще одна вещь, которую нужно реализовать это точка входа тестировщика (LoadBalancer или через Ingress), через которую можно получить доступ только к новой версии. Она может быть использована для просмотра вручную.


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


Также читайте другие статьи в нашем блоге:


Подробнее..

Из песочницы Тесты на pytest с генерацией отчетов в Allure с использованием Docker и Gitlab Pages и частично selenium

02.08.2020 00:20:53 | Автор: admin

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


Пример отчета, получающийся в allure


Когда я хотел добавить в gitlab автотесты в стеке python, allure, docker, то я выяснил, что толковых статей на эту тему нет. Пришлось разбираться самостоятельно и как результат проб и ошибок появилась эта статья, которая скорее является гайдом, частично затрагивающим написание тестов, но наибольший фокус именно на выстраивании инфраструктуры. Если у вас уже написаны тесты на allure, то вы сразу можете переходить к разделу настройки инфраструктуры. Отмечу, что текст НЕ затрагивает написание UI тестов, но я затрону инфраструктуру для них в отдельном блоке.


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


Этот гайд предполагает, что у вас уже установлен python, настроен пустой репозиторий, куда вы будете отправлять свой код. При чтении статьи не обязательно знать pytest, но очень желательно знать что из себя представляет фикстура, как ей пользоваться, а так же параметризация тестов. Для тестирования будет использован Dog API. Если вас интересует локальный запуск тестов через gitlab, то вам необходимо установить docker.


Если у вас не установлено вышеперечисленное ПО, то ниже вы можете найти полезные ссылки:



Содержание


  • Подготовка к написанию тестов
  • Написание фикстуры и небольшого клиента для API тестов
  • Пишем тесты
  • Добавляем allure в тесты
  • Делаем инфраструктуру для тестов
  • Настройка репозитория
  • Установка и настройка Gitlab Runner
  • Настройка пайплайна в .gitlab-ci.yml
  • Запуск тестов и просмотр отчета
  • Настройка пайплайна для UI тестов
  • Полезные ссылки

Подготовка к написанию тестов


Я назвал свой репозиторий для этой статьи allure_pages и скопировал его на свой компьютер.


Нужно создать такую структуру в директории с репозиторием:



  • conftest.py будет использован для написания фикстур
  • requirements.txt для установки необходимых модулей в контейнере с python
  • test_dog_api.py внутри отдельной директории tests для написания самих тестов

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


python -m venv venv


Открываем папку с репозиторием как проект в PyCharm



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


  1. Жмем на Add Interpreter и если вы ранее создали в директории с проектом виртуальное окружение, то PyCharm сразу найдет нужный интерпретатор, который будет использован в проекте
  2. Открываем терминал по этой кнопке


Устанавливаем нужные пакеты для наших тестов в виртуальном окружении:


Для этого введите в терминал следующие команды.


  • pip install requests для запросов, которые мы будем делать к сервису Dog Api
  • pip install pytest для тестов, само собой
  • pip install pytest-xdist для параллелизации тестов
  • pip install allure-pytest для генерации отчетов в allure

Очень важно после этого написать в терминале
pip freeze > requirements.txt.


Файл requirements.txt нам понадобится для запуска тестов внутри докер контейнера.

Написание фикстуры и небольшого клиента для API тестов


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


Сама фикстура


@pytest.fixturedef dog_api():    return ApiClient(base_address="http://personeltest.ru/aways/dog.ceo/api/")

Ниже конструктор класса для клиента. При инициализации объекта класс принимает в себя аргумент base_address, на его место нужно записать в фикстуре общий адрес для всех запросов в нашем случае будет использован https://dog.ceo/api/. Обратите внимание, что фикстура возвращает объект с базовым адресом для дальнейших GET и POST запросов.


class ApiClient:    def __init__(self, base_address):        self.base_address = base_address

Добавим в класс метод GET для отправки запросов.


    def get(self, path="/", params=None, headers=None):        url = f"{self.base_address}{path}"        return requests.get(url=url, params=params, headers=headers)

Здесь можно заметить, что наш клиент является оберткой над библиотекой requests. В классе ApiClient аргументы path, params, headers метода get выступают в роли аргументов, которые передаются в requests.get(url=url, params=params, headers=headers). Именно эта строчка и отвечает за выполнение запросов. Из адреса, использованного при инициализации объекта и пути, который мы передадим в дальнейшем в ходе теста, будет складываться полный адрес, который и будет использоваться в requests.get.


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


    def post(self, path="/", params=None, data=None, json=None, headers=None):        url = f"{self.base_address}{path}"        return requests.post(url=url, params=params, data=data, json=json, headers=headers)

В итоге у нас получается такой файл conftest.py:


import pytestimport requestsclass ApiClient:    def __init__(self, base_address):        self.base_address = base_address    def post(self, path="/", params=None, data=None, json=None, headers=None):        url = f"{self.base_address}{path}"        return requests.post(url=url, params=params, data=data, json=json, headers=headers)    def get(self, path="/", params=None, headers=None):        url = f"{self.base_address}{path}"        return requests.get(url=url, params=params, headers=headers)@pytest.fixturedef dog_api():    return ApiClient(base_address="http://personeltest.ru/aways/dog.ceo/api/")

Пишем тесты


В самом сервисе Dog API есть много разных запросов. Если у вас есть опыт в написании тестов, то можете самостоятельно написать несколько тестов. Так как акцент в этой статье заключается в постройке инфраструктуры для тестов, я не буду вдаваться в подробности тестов. Ранее я это сделал, чтобы мой код был более менее понятен. Несколько параметризованных тестов специально сломаны, чтобы показать как они будут показаны в отчете. Так выглядит мой файл с тестами test_dog_api.py


import pytestdef test_get_random_dog(dog_api):    response = dog_api.get("breeds/image/random")    with allure.step("Запрос отправлен, посмотрим код ответа"):        assert response.status_code == 200, f"Неверный код ответа, получен {response.status_code}"    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()        assert response["status"] == "success"    with allure.step(f"Посмотрим что получили {response}"):        with allure.step(f"Вложим шаги друг в друга по приколу"):            with allure.step(f"Наверняка получится что-то интересное"):                pass@pytest.mark.parametrize("breed", [    "afghan",    "basset",    "blood",    "english",    "ibizan",    "plott",    "walker"])def test_get_random_breed_image(dog_api, breed):    response = dog_api.get(f"breed/hound/{breed}/images/random")    response = response.json()    assert breed in response["message"], f"Нет ссылки на изображение с указанной породой, ответ {response}"@pytest.mark.parametrize("file", ['.md', '.MD', '.exe', '.txt'])def test_get_breed_images(dog_api, file):    response = dog_api.get("breed/hound/images")    response = response.json()    result = '\n'.join(response["message"])    assert file not in result, f"В сообщении есть файл с расширением {file}"@pytest.mark.parametrize("breed", [    "african",    "boxer",    "entlebucher",    "elkhound",    "shiba",    "whippet",    "spaniel",    "dvornyaga"])def test_get_random_breed_images(dog_api, breed):    response = dog_api.get(f"breed/{breed}/images/")    response = response.json()    assert response["status"] == "success", f"Не удалось получить список изображений породы {breed}"@pytest.mark.parametrize("number_of_images", [i for i in range(1, 10)])def test_get_few_sub_breed_random_images(dog_api, number_of_images):    response = dog_api.get(f"breed/hound/afghan/images/random/{number_of_images}")    response = response.json()    final_len = len(response["message"])    assert final_len == number_of_images, f"Количество фото не {number_of_images}, а {final_len}"

Добавляем allure в тесты


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


with allure.step('step 1'): с помощью этого контекстного менеджера тело теста делится на шаги, понятные в отчете.
@allure.feature('Dog Api') @allure.story('Send few requests') декораторы, с помощью которых сами тесты или тестовые наборы будут структурированы в отчете


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

Добавим в наш API клиент несколько allure шагов.


class ApiClient:    def __init__(self, base_address):        self.base_address = base_address    def post(self, path="/", params=None, data=None, json=None, headers=None):        url = f"{self.base_address}{path}"        with allure.step(f'POST request to: {url}'):            return requests.post(url=url, params=params, data=data, json=json, headers=headers)    def get(self, path="/", params=None, headers=None):        url = f"{self.base_address}{path}"        with allure.step(f'GET request to: {url}'):            return requests.get(url=url, params=params, headers=headers)

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


@allure.feature('Random dog')@allure.story('Получение фото случайной собаки и вложенные друг в друга шаги')def test_get_random_dog(dog_api):    response = dog_api.get("breeds/image/random")    with allure.step("Запрос отправлен, посмотрим код ответа"):        assert response.status_code == 200, f"Неверный код ответа, получен {response.status_code}"    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()        assert response["status"] == "success"    with allure.step(f"Посмотрим что получили {response}"):        with allure.step(f"Вложим шаги друг в друга по приколу"):            with allure.step(f"Наверняка получится что-то интересное"):                pass

Сходным образом заполним шагами и декораторами остальные тесты. Финальный файл test_dog_api.py


import pytestimport allure@allure.feature('Random dog')@allure.story('Получение фото случайной собаки и вложенные друг в друга шаги')def test_get_random_dog(dog_api):    response = dog_api.get("breeds/image/random")    with allure.step("Запрос отправлен, посмотрим код ответа"):        assert response.status_code == 200, f"Неверный код ответа, получен {response.status_code}"    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()        assert response["status"] == "success"    with allure.step(f"Посмотрим что получили {response}"):        with allure.step(f"Вложим шаги друг в друга по приколу"):            with allure.step(f"Наверняка получится что-то интересное"):                pass@allure.feature('Random dog')@allure.story('Фото случайной собаки из определенной породы')@pytest.mark.parametrize("breed", [    "afghan",    "basset",    "blood",    "english",    "ibizan",    "plott",    "walker"])def test_get_random_breed_image(dog_api, breed):    response = dog_api.get(f"breed/hound/{breed}/images/random")    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()    assert breed in response["message"], f"Нет ссылки на фото с указанной породой, ответ {response}"@allure.feature('List of dog images')@allure.story('Список всех фото собак списком содержит только изображения')@pytest.mark.parametrize("file", ['.md', '.MD', '.exe', '.txt'])def test_get_breed_images(dog_api, file):    response = dog_api.get("breed/hound/images")    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()    with allure.step("Соединим все ссылки в ответе из списка в строку"):        result = '\n'.join(response["message"])    assert file not in result, f"В сообщении есть файл с расширением {file}"@allure.feature('List of dog images')@allure.story('Список фото определенных пород')@pytest.mark.parametrize("breed", [    "african",    "boxer",    "entlebucher",    "elkhound",    "shiba",    "whippet",    "spaniel",    "dvornyaga"])def test_get_random_breed_images(dog_api, breed):    response = dog_api.get(f"breed/{breed}/images/")    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()    assert response["status"] == "success", f"Не удалось получить список изображений породы {breed}"@allure.feature('List of dog images')@allure.story('Список определенного количества случайных фото')@pytest.mark.parametrize("number_of_images", [i for i in range(1, 10)])def test_get_few_sub_breed_random_images(dog_api, number_of_images):    response = dog_api.get(f"breed/hound/afghan/images/random/{number_of_images}")    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()    with allure.step("Посмотрим длину списка со ссылками на фото"):        final_len = len(response["message"])    assert final_len == number_of_images, f"Количество фото не {number_of_images}, а {final_len}"

Делаем инфраструктуру для тестов


Здесь есть несколько моментов, о которых стоит написать:


  • .gitlab-ci.yml файл, где на языке разметки yaml описаны инструкции что нужно делать gitlab runner
  • gitlab-runner это проект, написанный на языке Go. Он будет выполнять инструкции. Есть несколько вариантов его использования. Мы будем писать инструкцию для gitlab runner, где он в свою очередь будет использовать docker для запуска тестов и всего остального. Далее по тексту он будет обозначаться просто "раннер". Что это за сущность и с чем её едят можно найти здесь.

Если у вас в компании уже настроена инфраструктура и вы можете попросить своего devops выделить вам раннер где-то в облаке, то сделайте это. Раннер можно использовать и локально на своем компьютере. В этом случае вам нужен установленный docker desktop на windows. В этом гайде мы будем использовать локальный раннер, но для облачного запуска вам нужно будет просто указать в .gitlab-ci нужный раннер.


Если у вас нет docker desktop, то здесь инструкция как его заиметь.

Настройка репозитория


Нужно зайти в настройки репозитория Settings -> General -> Visibility, project features, permissions, активировать Pipelines и сохранить изменения.



После этого в разделе настроек появится раздел CI / CD. Переходим Settings -> CI / CD -> Runners



Здесь переходим по ссылке в пункте 1 и согласно инструкции ставим себе на компьютер Gitlab Runner. Далее в статье адаптация инструкции на русском.


Установка и настройка Gitlab Runner


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


  1. Создайте папку где-нибудь в системе, например: C:\GitLab-Runner.
  2. Скачайте бинарник для x86 или amd64 и положите его в созданную папку. Переименуйте скачанный файл в gitlab-runner.exe. Вы можете скачать бинарник для любой доступной версии здесь
  3. Запустите командную строку с правами администратора. (Сам я буду использовать обычный powershell с правами администратора)
  4. Зарегестрируйте раннер
  5. Установите раннер как сервис и запустите его. Вы можете запустить службу, используя встроенную системную учетную запись (рекомендуется) или учетную запись пользователя.

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


Я создал папку C:\gitlab_runners и сохранил туда скачанный для моей системы раннер, предварительно переименовав в gitlab-runner.exe


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


  1. Ввести команду:
    ./gitlab-runner.exe register
  2. Ввести url, указанный на странице настроек раннеров в репозитори в пункте 2. Например, у меня так:
    https://gitlab.somesubdomain.com/
  3. Ввести токен указанный на странице настроек раннеров в репозитори в пункте 3. Например, у меня так:
    tJTUaJ7JxfL4yafEyF3k
  4. Вводим описание раннера. Его потом можно будет изменить через UI в настроках раннера в репозитории. Например так:
    Runner on windows for autotests
  5. Добавляем теги для раннера, для того, чтобы описывать нужный раннер в .gitlab-ci.yml, используя определенные теги. Это в основном нужно, когда раннеров больше одного.
    docker, windows
  6. Выбираем тип раннера, который нам нужен. Здесь выберем docker
    docker
  7. Вводим дефолтный image, который будет использоваться раннером. Его можно будет изменить в конфиге раннера или указать конкретый в .gitlab-ci.yml
    python:3.8-alpine

Скрншот с выполнеными шагами



Наш раннер зарегистрирован, теперь его нужно запустить.


Проверить статус раннера можно так:
.\gitlab-runner.exe status


Запустите раннер с помощью:
.\gitlab-runner.exe run


Теперь, если вы зайдете в настроки раннеров в репозитории Settings -> CI / CD -> Runners, то увидите что-то такое:



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



Все хорошо, теперь можно перейти к пайплайну.


Настройка пайплайна в .gitlab-ci.yml


Инструкции для раннера описываются в .gitlab-ci.yml. Полная документация описана здесь.


Первым делом нам нужно описать stages. Шаги пайплайна. Их будет 4. Каждый stage отдельный job, который будет выполнять раннер.


stages:  - testing # Запуск тестов  - history_copy # Копирование результата тестов из предыдущего запуска тестов  - reports # Генерация отчета  - deploy # Публикация отчета на gitlab pages

Шаг первый. Testing


docker_job: # Название job  stage: testing # Первый stage, который нужно выполнить  tags:    - docker # С помощью этого тега gitlab поймет, какой раннер нужно запустить. Он запустит докер контейнер, из образа, который мы указывали в 6 шаге регистрации раннера.  before_script:    - pip install -r requirements.txt # Устанавливаем пакеты в поднятом контейнере перед запуском самих тестов  script:    - pytest -n=4 --alluredir=./allure-results tests/test_dog_api.py # Запускаем тесты параллельно(-n=4 обеспечивает нам это), указав папку с результатами тестов через --alluredir=  allow_failure: true # Это позволит нам продолжить выполнение пайплайна в случае, если тесты упали.  artifacts: # Сущность, с помощью которой, мы сохраним результат тестирования.    when: always # Сохранять всегда    paths:      - ./allure-results # Здесь будет сохранен отчет    expire_in: 1 day # Да, он будет удален через день. Нет смысла хранить его в течение длительного срока.

Шаг второй. history_copy


history_job: # Название job  stage: history_copy # Это второй stage, который нужно выполнить  tags:    - docker # Пользуемся тем же самым раннером  image: storytel/alpine-bash-curl # Но теперь укажем раннеру использовать другой образ, для того чтобы скачать результаты теста из предыдущего пайплайна. Нам же нужна история тестов, верно?  script:    - 'curl --location --output artifacts.zip "https://gitlab.smarthead.ru/api/v4/projects/(АЙДИ ВАШЕГО РЕПОЗИТОРИЯ)/jobs/artifacts/master/download?job=pages&job_token=$CI_JOB_TOKEN"'  # С помощью api гитлаба скачиваем файлы из job, который будет указан ниже. Обратите внимание на текст на русском в ссылке. Очень важно указать вместо текста и скобок номер вашего репозиториия    - unzip artifacts.zip # Распаковываем файлы    - chmod -R 777 public # Даем права любые манипуляции с содержимым    - cp -r ./public/history ./allure-results # Копируем историю в папку с результатами теста  allow_failure: true # Так как при первом запуске пайплайна истории нет, это позволит нам избежать падения пайплайна. В дальнейшем эту строчку можно спокойно удалить.  artifacts:     paths:      - ./allure-results # Сохраняем данные    expire_in: 1 day  rules:    - when: always # Сохранять всегда

Шаг третий. reports


allure_job: # Название job  stage: reports # Третий stage, который будет выполнен  tags:    - docker # Пользуемся тем же самым раннером  image: frankescobar/allure-docker-service # Указываем раннеру использовать образ с allure. В нем мы будем генерировать отчет.  script:     - allure generate -c ./allure-results -o ./allure-report # Генерируем отчет из ./allure-results внутрь папки ./allure-report  artifacts:    paths:      - ./allure-results # Примонтируем две этих директории для получения результатов тестирования и генерации отчетов соответственно      - ./allure-report    expire_in: 1 day  rules:    - when: always

Шаг четвертый. deploy


pages: # Названием этой job говорим гитлабу, чтобы захостил результат у себя в pages  stage: deploy # Четвертый stage, который будет выполнен  script:    - mkdir public # Создаем папку public. По умолчанию гитлаб хостит в gitlab pages только из папки public    - mv ./allure-report/* public # Перемещаем в папку public сгенерированный отчет.  artifacts:    paths:      - public  rules:    - when: always

Финальный .gitlab-ci.yml


stages:  - testing # Запуск тестов  - history_copy # Копирование результата тестов из предыдущего запуска тестов  - reports # Генерация отчета  - deploy # Публикация отчета на gitlab pagesdocker_job: # Название job  stage: testing # Первый stage, который нужно выполнить  tags:    - docker # С помощью этого тега gitlab поймет, какой раннер нужно запустить. Он запустит докер контейнер, из образа, который мы указывали в 6 шаге регистрации раннера.  before_script:    - pip install -r requirements.txt # Устанавливаем пакеты в поднятом контейнере перед запуском самих тестов  script:    - pytest -n=4 --alluredir=./allure-results tests/test_dog_api.py # Запускаем тесты параллельно(-n=4 обеспечивает нам это), указав папку с результатами тестов через --alluredir=  allow_failure: true # Это позволит нам продолжить выполнение пайплайна в случае, если тесты упали.  artifacts: # Сущность, с помощью которой, мы сохраним результат тестирования.    when: always # Сохранять всегда    paths:      - ./allure-results # Здесь будет сохранен отчет    expire_in: 1 day # Да, он будет удален через день. Нет смысла хранить его в течение длительного срока.history_job: # Название job  stage: history_copy # Это второй stage, который нужно выполнить  tags:    - docker # Пользуемся тем же самым раннером  image: storytel/alpine-bash-curl # Но теперь укажем раннеру использовать другой образ, для того чтобы скачать результаты теста из предыдущего пайплайна. Нам же нужна история тестов, верно?  script:    - 'curl --location --output artifacts.zip "https://gitlab.smarthead.ru/api/v4/projects/(АЙДИ ВАШЕГО РЕПОЗИТОРИЯ)/jobs/artifacts/master/download?job=pages&job_token=$CI_JOB_TOKEN"'  # С помощью api гитлаба скачиваем файлы из job, который будет указан ниже. Обратите внимание на текст на русском в ссылке. Очень важно указать вместо текста и скобок номер вашего репозиториия    - unzip artifacts.zip # Распаковываем файлы    - chmod -R 777 public # Даем права любые манипуляции с содержимым    - cp -r ./public/history ./allure-results # Копируем историю в папку с результатами теста  allow_failure: true # Так как при первом запуске пайплайна истории нет, это позволит нам избежать падения пайплайна. В дальнейшем эту строчку можно спокойно удалить.  artifacts:     paths:      - ./allure-results # Сохраняем данные    expire_in: 1 day  rules:    - when: always # Сохранять всегдаallure_job: # Название job  stage: reports # Третий stage, который будет выполнен  tags:    - docker # Пользуемся тем же самым раннером  image: frankescobar/allure-docker-service # Указываем раннеру использовать образ с allure. В нем мы будем генерировать отчет.  script:     - allure generate -c ./allure-results -o ./allure-report # Генерируем отчет из ./allure-results внутрь папки ./allure-report  artifacts:    paths:      - ./allure-results # Примонтируем две этих директории для получения результатов тестирования и генерации отчетов соответственно      - ./allure-report    expire_in: 1 day  rules:    - when: alwayspages: # Названием этой job говорим гитлабу, чтобы захостил результат у себя в pages  stage: deploy # Четвертый stage, который будет выполнен  script:    - mkdir public # Создаем папку public. По умолчанию гитлаб хостит в gitlab pages только из папки public    - mv ./allure-report/* public # Перемещаем в папку public сгенерированный отчет.  artifacts:    paths:      - public  rules:    - when: always

Запуск тестов и просмотр отчета


Осталось отправить в репозиторий все необходимые файлы, а именно:


  1. conftest.py
  2. Директорию tests
  3. requirements.txt (Убедитесь, что там есть все нужные зависимости)
  4. .gitlab-ci.yml

Отправляем и смотрим что получилось.


В первый раз пайплайн будет запускаться немного дольше, чем в следующие разы. Потому что ему нужно будет скачать нужные образы, запустить их и все такое. Заходим в репозиторий и через сайдбар переходим в CI / CD -> Pipelines


Здесь вы сможете увидеть статус пайплайна, а также перейти к Ci линтеру, где можете проверить конкретно свой .gitlab-ci.yml.




Когда пайплайн пройдет, увидеть ссылку и сам отчет можно через Settings -> Pages. При первом использовании pages в репозитории для просмотра страницы может понадобиться до 30 минут. Проходим по ссылке и смотрим отчет.



Получаем результат. Советую побродить по отчету и посмотреть результаты.



Сломаем несколько тестов, чтобы посмотреть, как будет отображаться история тестов. Затем подождем окончания пайплайна.



Как видите второй шаг в этот раз выполнился без ошибок. Это потому, на stage history_job проблем с копированием истории предыдущих пайплайнов не возникло. Теперь посмотрим результат тестов.



Видна динамика, количество упавших тестов и все остальное. Для просмотра подробностей по тестам рекомендую посмотреть отчет самостоятельно.



Настройка пайплайна для UI тестов


Реализовать их можно с помощью специальной сущности гитлаба [services](http://personeltest.ru/aways/docs.gitlab.com/ee/ci/services/). Это ключевое слово для использования докер образов, которые будут запущены до хода шагов в script. Для UI тестов нужно добавить в job пайплайна следующее:


  services:    - selenium/standalone-chrome:latest

Так как нам нужно обращаться к контейнеру, который поднимет гитлаб по определенному url, то он каким-то образом назначает этому сервису url, по которому можно слать запросы. Логика здесь такая:


  • Все после : отбрасывается
  • Слеш / заменяется двойным подчеркиванием __ и создается главный алиас
  • Слеш / заменяется одиночным дефисом - и создается дополнительный алиас (Необходим Gitlab Runner v1.1.0 и выше)

И теперь нужно изменить в фикстуре executor (по умолчанию используется chromedriver на вашей машине, но сейчас мы все это запускаем в контейнерах) для запуска ui тестов:


browser = webdriver.Remote(command_executor="http://personeltest.ru/away/selenium__standalone-chrome:4444/wd/hub")

Обратите внимание, что было в .gitlab-ci.yml:
selenium/standalone-chrome:latest
И по какому адресу нужно обращаться фикстуре для запуска тестов:
selenium__standalone-chrome


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


chrome_job:  stage: testing  services:    - selenium/standalone-chrome  image: python:3.8  tags:    - docker  before_script:    - pip install -r requirements.txt  script:    - pytest --alluredir=./allure-results tests/  allow_failure: true  artifacts:    when: always    paths:      - ./allure-results    expire_in: 1 day

Полезные ссылки


Помимо ссылок в статье, хочется поделиться еще несколькими, которые немного разъяснять вам что такое allure и как писать gitlab ci.


Статья об allure на хабре
Введение в gitlab ci

Подробнее..

Перевод Самый ужасный день в компании Slack

08.07.2020 18:05:40 | Автор: admin


Эта статья описывает технические детали проблем, из-за которых Slack упал 12 мая 2020 года. Больше о процессе реагирования на тот инцидент см. хронологию Райана Каткова Обе руки на пульте.

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

Пользователи заметили даунтайм в 16:45 по тихоокеанскому времени, но на самом деле история началась около 8:30 утра. Команда разработки по надёжности БД (Database Reliability Engineering Team) получила предупреждение о значительном увеличении нагрузки на часть инфраструктуры. В то же время команда по трафику (Traffic Team) получила предупреждения, что мы не выполняем некоторые запросы API.

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

Одним из последствий инцидента стало значительное расширение нашего основного уровня веб-приложений. Наш генеральный директор Стюарт Баттерфилд писал о некотором влиянии карантина и самоизоляции на использование Slack. В результате пандемии мы запустили значительно больше инстансов на уровне веб-приложений, чем в далёком феврале этого года. Мы быстро масштабируемся, когда воркеры нагружаются, как это произошло здесь но воркеры гораздо дольше ждали завершения некоторых запросов к БД, что вызвало более высокую загрузку. Во время инцидента мы увеличили количество инстансов на 75%, что привело к максимальному количеству хостов веб-приложений, которые мы когда-либо запускали до сегодняшнего дня.

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

За балансировщиком нагрузки на 4-м уровне стоит набор инстансов HAProxy для распределения запросов на уровень веб-приложений. Мы используем Consul для обнаружения служб и consul-template для рендеринга списков здоровых бэкендов веб-приложений, к которым HAProxy должен направлять запросы.


Рис. 1. Высокоуровневое представление архитектуры балансировки нагрузки Slack

Однако мы не рендерим список хостов веб-приложений прямо из конфигурационного файла HAProxy, потому что обновление списка в таком случае требует перезагрузки HAProxy. Процесс перезагрузки HAProxy включает в себя создание совершенно нового процесса, сохраняя при этом старый, пока он не закончит работу с текущими запросами. Очень частые перезагрузки могут привести к слишком большому количеству запущенных процессов HAProxy и низкой производительности. Это ограничение находится в противоречии с целью автоматического масштабирования уровня веб-приложений, которая заключается в том, чтобы как можно быстрее вводить в эксплуатацию новые инстансы. Поэтому мы используем HAProxy Runtime API для управления состоянием сервера HAProxy без перезагрузки каждый раз, когда сервер веб-уровня входит в эксплуатацию или выходит из неё. Стоит отметить, что HAProxy может интегрироваться с DNS-интерфейсом Consul, но это добавляет лаг из-за TTL DNS, ограничивает возможность использования тегов Consul, а управление очень большими ответами DNS часто приводит к болезненным пограничным ситуациям и ошибкам.


Рис. 2. Как набор бэкендов веб-приложений управляется на одном сервере Slack HAProxy

В нашем состоянии HAProxy мы определяем шаблоны серверов HAProxy. Фактически, это слоты, которые могут занимать бэкенды веб-приложений. Когда выкатывается инстанс нового веб-приложения или старый начинает отказывать, обновляется каталог сервисов Consul. Consul-template выводит новую версию списка хостов, а отдельная программа haproxy-server-state-management, разработанная в Slack, считывает этот список хостов и использует HAProxy Runtime API для обновления состояния HAProxy.

Мы запускаем M параллельных пулов инстансов HAProxy и веб-приложений, каждый пул в отдельной зоне доступности AWS. HAProxy сконфигурирован с N слотами для бэкендов веб-приложений в каждой зоне доступности (AZ), что даёт в общей сложности N*M бэкендов, которые могут быть направлены на все AZ. Несколько месяцев назад это количество было более чем достаточным мы никогда не запускали ничего даже близко к такому количеству инстансов нашего уровня веб-приложений. Однако после утреннего инцидента с базой данных мы запустили чуть больше, чем N*M инстансов веб-приложений. Если представить слоты HAProxy как гигантскую игру в стулья, то некоторые из этих инстансов webapp остались без места. Это не было проблемой у нас более чем достаточно возможностей для обслуживания.

Рис. 3. Слоты в процессе HAProxy с некоторыми избыточными экземплярами веб-приложений, которые не получают трафик

Однако в течение дня возникла проблема. Выявился баг в программе, которая синхронизировала список хостов, сгенерированный consul-template, с состоянием сервера HAProxy. Программа всегда пыталась найти слот для новых инстансов webapp, прежде чем освободить слоты, занятые старыми инстансами webapp, которые больше не работают. Эта программа начала выдавать ошибки и рано завершать работу, потому что не могла найти ни одного пустого слота, а это означало, что запущенные инстансы HAProxy не обновляли своё состояние. В течение дня группа автомасштабирования webapp увеличивалась и уменьшалась, а список бэкендов в состоянии HAProxy всё больше устаревал.

В 16:45 большинство инстансов HAProxy были способны отправлять запросы только к набору бэкендов, доступных утром, и этот набор старых бэкендов webapp теперь составлял меньшинство. Мы регулярно предоставляем новые инстансы HAProxy, так что оставалось несколько свежих с правильной конфигурацией, но большинство из них оказались старше восьми часов и поэтому застряли с полным и устаревшим состоянием бэкенда. В конечном итоге, произошёл сбой сервиса. Это случилось в конце рабочего дня в США, потому что именно тогда мы начинаем масштабировать уровень веб-приложений по мере снижения трафика. Автомасштабирование в первую очередь завершает работу старых инстансов webapp, и это означало, что в серверном состоянии HAProxy их осталось недостаточно для обслуживания спроса.

Рис. 4. Состояние HAProxy изменялось с течением времени и слоты начали ссылаться в основном на удалённые хосты

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

Мы особо не переделывали этот стек HAProxy, потому что всю балансировку нагрузки постепенно переводим на Envoy (недавно мы перенесли на него трафик веб-сокетов). HAProxy хорошо и надёжно служил в течение многих лет, но у него есть некоторые операционные проблемы, как в этом инциденте. Сложный конвейер для управления состоянием сервера HAProxy мы заменим собственной интеграцией Envoy с плоскостью управления xDS для обнаружения конечных точек. Самые последние версии HAProxy (начиная с версии 2.0) тоже решают многие из этих операционных проблем. Тем не менее, мы уже некоторое время доверяем Envoy внутреннюю сервисную сетку, поэтому стремимся и балансировку нагрузки тоже перевести на него. Наше первоначальное тестирование Envoy+ xDS в масштабе выглядит многообещающе, и в будущем эта миграция должна улучшить как производительность, так и доступность. Новая архитектура балансировки нагрузки и обнаружения служб не восприимчива к проблеме, вызвавшей этот сбой.

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

Как восстановить Sentry после не удачного обновления

22.07.2020 22:18:15 | Автор: admin
Всем привет. Я хочу рассказать о том, как проходило восстановление Sentry после неудачного обновления.

Что же такое Sentry?

Это система полного отслеживания ошибок с открытым исходным кодом, которая поддерживает широкий спектр серверных, браузерных, настольных и родных мобильных языков и сред, включая PHP, Node. js, Python, Ruby, C #, Java, Go, React, Angular, Vue, JavaScript и другие.

Немного о том как просто и не принуждённо всё поломалось.

Жили мы на версии 9.0.0 и пришло время обновиться. Пощупав web интерфейс 10.0.0 принял решение обновиться на неё. Это была самая большая ошибка!

Обновление прошло в штатном режиме. Сначала переход на 9.1.2 затем на 10.0.0 (иначе нельзя). Далее обнаружил что не запускается worker. Причиной этому добавление новых приложений и зависимостей.

Попытки собрать приложения и засунуть их в k8s не увенчались успехом.

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

После 3 часов работ и привлечения DBA эксперта данные были восстановлены до рабочего состояния.

Как?

Сделали рядом инстанс PG, закинули туда схему от версии 9.0.0 и начали потабличное восстановление. Конечно без ошибок и напильника тут не обошлось.

На этом история не заканчивается. Пришло время всё же обновиться, на версию 9.1.2.

Собрал образ, запускаю:

sentry upgrade

и видим вот такое чудесное сообщение:

 ! I'm not trusting myself; either fix this yourself by fiddling ! with the south_migrationhistory table, or pass --delete-ghost-migrations ! to South to have it delete ALL of these records (this may not be good).Exception in thread Thread-1 (most likely raised during interpreter shutdown)

После долгих поисков, проб и ошибок было найдено решение.

Необходимо восстановить схему данных текущей версии.

Для этого клонируем репозиторий:

git clone https://github.com/getsentry/onpremise.git

Переходим в ветку 9.1.2, так как ветки 9.0.0 уже нет.

cd onpremise && git checkout tags/9.1.2

Далее правим образ в Dockerfile:

#cat Dockerfile ARG SENTRY_IMAGEFROM ${SENTRY_IMAGE:-sentry:9.0.0}-onbuild
Затем правим compose file

docker-compose.yml
# NOTE: This docker-compose.yml is meant to be just an example of how# you could accomplish this on your own. It is not intended to work in# all use-cases and must be adapted to fit your needs. This is merely# a guideline.# See docs.getsentry.com/on-premise/server/ for full# instructionsversion: '3.4'x-defaults: &defaults  restart: unless-stopped  build:    context: .  depends_on:    - redis   # - postgres    - memcached    - smtp  env_file: .env  environment:    SENTRY_MEMCACHED_HOST: memcached    SENTRY_REDIS_HOST: redis    SENTRY_POSTGRES_HOST: 'sentry.cl.ats'    SENTRY_DB_USER: 'sentryDB'    SENTRY_DB_NAME: 'sentry_user'    SENTRY_DB_PASSWORD: 'sentry_passwd'    SENTRY_POSTGRES_PORT: 5432    SENTRY_EMAIL_HOST: smtp  volumes:    - sentry-data:/var/lib/sentry/filesservices:  smtp:    restart: unless-stopped    image: tianon/exim4  memcached:    restart: unless-stopped    image: memcached:1.5-alpine  redis:    restart: unless-stopped    image: redis:3.2-alpine  #postgres:  #  restart: unless-stopped  #  image: postgres:9.5  #  environment:  #    POSTGRES_HOST_AUTH_METHOD: 'trust'  #  volumes:  #    - sentry-postgres:/var/lib/postgresql/data  web:    <<: *defaults    ports:      - '9000:9000'  cron:    <<: *defaults    command: run cron  worker:    <<: *defaults    command: run workervolumes:    sentry-data:      external: true    #sentry-postgres:    #  external: true


Немного пояснений. Так как у меня есть отдельный инстанс базы данных, то создание через compose пропускаю, так же ненужен volume sentry-postgres.

После данных манипуляций запускаем сборку:

./install.sh 

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

docker-compose run --rm web sentry django migrate --delete-ghost-migrations

Примерный вывод
Creating network "onpremise_default" with the default driverCreating onpremise_memcached_1 ... doneCreating onpremise_smtp_1      ... doneCreating onpremise_redis_1     ... done21:30:26 [INFO] sentry.plugins.github: apps-not-configuredRunning migrations for sentry:- Nothing to migrate. - Loading initial data for sentry.Installed 0 object(s) from 0 fixture(s)Running migrations for nodestore:- Nothing to migrate. - Loading initial data for nodestore.Installed 0 object(s) from 0 fixture(s)Running migrations for search:- Nothing to migrate. - Loading initial data for search.Installed 0 object(s) from 0 fixture(s)Running migrations for social_auth:- Nothing to migrate. - Loading initial data for social_auth.Installed 0 object(s) from 0 fixture(s)Running migrations for tagstore:- Nothing to migrate. - Loading initial data for tagstore.Installed 0 object(s) from 0 fixture(s)Running migrations for jira_ac:- Nothing to migrate. - Loading initial data for jira_ac.Installed 0 object(s) from 0 fixture(s)Running migrations for hipchat_ac:- Nothing to migrate. - Loading initial data for hipchat_ac.Installed 0 object(s) from 0 fixture(s)


Далее можно запускать upgrade.

docker-compose run --rm web sentry upgrade

Stdout
Starting onpremise_smtp_1      ... doneStarting onpremise_memcached_1 ... doneStarting onpremise_redis_1     ... done21:30:48 [INFO] sentry.plugins.github: apps-not-configuredSyncing...Creating tables ...Installing custom SQL ...Installing indexes ...Installed 0 object(s) from 0 fixture(s)Synced: > django.contrib.admin > django.contrib.auth > django.contrib.contenttypes > django.contrib.messages > django.contrib.sessions > django.contrib.sites > django.contrib.staticfiles > crispy_forms > debug_toolbar > raven.contrib.django.raven_compat > rest_framework > sentry.plugins.sentry_interface_types > sentry.plugins.sentry_mail > sentry.plugins.sentry_urls > sentry.plugins.sentry_useragents > sentry.plugins.sentry_webhooks > sudo > south > sentry_plugins.slackNot synced (use migrations): - sentry - sentry.nodestore - sentry.search - social_auth - sentry.tagstore - sentry_plugins.jira_ac - sentry_plugins.hipchat_ac(use ./manage.py migrate to migrate these)Running migrations for sentry:- Nothing to migrate. - Loading initial data for sentry.Installed 0 object(s) from 0 fixture(s)Running migrations for nodestore:- Nothing to migrate. - Loading initial data for nodestore.Installed 0 object(s) from 0 fixture(s)Running migrations for search:- Nothing to migrate. - Loading initial data for search.Installed 0 object(s) from 0 fixture(s)Running migrations for social_auth:- Nothing to migrate. - Loading initial data for social_auth.Installed 0 object(s) from 0 fixture(s)Running migrations for tagstore:- Nothing to migrate. - Loading initial data for tagstore.Installed 0 object(s) from 0 fixture(s)Running migrations for jira_ac:- Nothing to migrate. - Loading initial data for jira_ac.Installed 0 object(s) from 0 fixture(s)Running migrations for hipchat_ac:- Nothing to migrate. - Loading initial data for hipchat_ac.Installed 0 object(s) from 0 fixture(s)Creating missing DSNsCorrecting Group.num_comments counter


И так, схема восстановлена. Можно обновиться на 9.1.2.

Для этого в Dockerfile меняем версию на 9.1.2:

ARG SENTRY_IMAGEFROM ${SENTRY_IMAGE:-sentry:9.1.2}-onbuild
И запускаем install.sh

./install.sh

Если всё сделано верно, то процесс завершится без ошибок. Вывод должен быть приметно такой:

Stdout
# ./install.sh Checking minimum requirements...Creating volumes for persistent storage...Created sentry-data..env already exists, skipped creation.Building and tagging Docker images...smtp uses an image, skippingmemcached uses an image, skippingredis uses an image, skippingBuilding webStep 1/2 : ARG SENTRY_IMAGEStep 2/2 : FROM ${SENTRY_IMAGE:-sentry:9.1.2}-onbuild# Executing 4 build triggers ---> Running in 6c97f9fcaf63DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-supportRemoving intermediate container 6c97f9fcaf63 ---> Running in 9e0f65ee3af6Removing intermediate container 9e0f65ee3af6 ---> Running in 09754c44319cRemoving intermediate container 09754c44319c ---> a12fa31c2675Successfully built a12fa31c2675Successfully tagged onpremise_web:latestBuilding cronStep 1/2 : ARG SENTRY_IMAGEStep 2/2 : FROM ${SENTRY_IMAGE:-sentry:9.1.2}-onbuild# Executing 4 build triggers ---> Using cache ---> Using cache ---> Using cache ---> Using cache ---> a12fa31c2675Successfully built a12fa31c2675Successfully tagged onpremise_cron:latestBuilding workerStep 1/2 : ARG SENTRY_IMAGEStep 2/2 : FROM ${SENTRY_IMAGE:-sentry:9.1.2}-onbuild# Executing 4 build triggers ---> Using cache ---> Using cache ---> Using cache ---> Using cache ---> a12fa31c2675Successfully built a12fa31c2675Successfully tagged onpremise_worker:latestDocker images built.Generating secret key...Secret key written to .envSetting up database...Starting onpremise_smtp_1      ... doneStarting onpremise_redis_1     ... doneStarting onpremise_memcached_1 ... done21:35:07 [WARNING] sentry.utils.geo: settings.GEOIP_PATH_MMDB not configured.21:35:10 [INFO] sentry.plugins.github: apps-not-configuredSyncing...Creating tables ...Installing custom SQL ...Installing indexes ...Installed 0 object(s) from 0 fixture(s)Migrating...Running migrations for sentry: - Migrating forwards to 0472_auto__add_field_sentryapp_author. > sentry:0424_auto__add_field_integration_status > sentry:0425_auto__add_index_pullrequest_organization_id_merge_commit_sha > sentry:0425_remove_invalid_github_idps > sentry:0426_auto__add_sentryappinstallation__add_sentryapp__add_field_user_is_sent > sentry:0427_auto__add_eventattachment__add_unique_eventattachment_project_id_event > sentry:0428_auto__add_index_eventattachment_project_id_date_added > sentry:0429_auto__add_integrationexternalproject__add_unique_integrationexternalpr > sentry:0430_auto__add_field_organizationintegration_status > sentry:0431_auto__add_field_externalissue_metadata > sentry:0432_auto__add_field_relay_is_internal > sentry:0432_auto__add_index_userreport_date_added__add_index_eventattachment_date_ > sentry:0433_auto__add_field_relay_is_internal__add_field_userip_country_code__add_ > sentry:0434_auto__add_discoversavedqueryproject__add_unique_discoversavedqueryproj > sentry:0435_auto__add_field_discoversavedquery_created_by > sentry:0436_rename_projectdsymfile_to_projectdebugfile > sentry:0437_auto__add_field_sentryapp_status > sentry:0438_auto__add_index_sentryapp_status__chg_field_sentryapp_proxy_user__chg_ > sentry:0439_auto__chg_field_sentryapp_owner > sentry:0440_auto__del_unique_projectdebugfile_project_debug_id__add_index_projectd > sentry:0441_auto__add_field_projectdebugfile_data > sentry:0442_auto__add_projectcficachefile__add_unique_projectcficachefile_project_ > sentry:0443_auto__add_field_organizationmember_token_expires_at > sentry:0443_auto__del_dsymapp__del_unique_dsymapp_project_platform_app_id__del_ver > sentry:0444_auto__add_sentryappavatar__add_field_sentryapp_redirect_url__add_field > sentry:0445_auto__add_promptsactivity__add_unique_promptsactivity_user_feature_org > sentry:0446_auto__add_index_promptsactivity_project_id > sentry:0447_auto__del_field_promptsactivity_organization__add_field_promptsactivit > sentry:0448_auto__add_field_sentryapp_is_alertable > sentry:0449_auto__chg_field_release_owner > sentry:0450_auto__del_grouphashtombstone__del_unique_grouphashtombstone_project_ha > sentry:0451_auto__del_field_projectbookmark_project_id__add_field_projectbookmark_ > sentry:0452_auto__add_field_sentryapp_events > sentry:0452_auto__del_field_releaseenvironment_organization_id__del_field_releasee > sentry:0453_auto__add_index_releasefile_release_name > sentry:0454_resolve_duplicate_0452 > sentry:0455_auto__add_field_groupenvironment_first_seen > sentry:0456_auto__add_dashboard__add_unique_dashboard_organization_title__add_widg > sentry:0457_auto__add_field_savedsearch_is_global__chg_field_savedsearch_project__ > sentry:0457_auto__add_monitorcheckin__add_monitor__add_index_monitor_type_next_che > sentry:0458_global_searches_data_migrationSaved Searchs: 100% |########################################################################################################################################################################| Time: 0:00:07 > sentry:0459_global_searches_unique_constraint > sentry:0460_auto__add_field_servicehook_organization_id > sentry:0461_event_attachment_indexes > sentry:0462_auto__add_servicehookproject > sentry:0462_releaseenvironment_project_id > sentry:0463_backfill_service_hook_project > sentry:0464_auto__add_sentryappcomponent__add_field_sentryapp_schema > sentry:0464_groupenvironment_foreignkeys > sentry:0465_sync > sentry:0466_auto__add_platformexternalissue__add_unique_platformexternalissue_grou > sentry:0467_backfill_integration_status > sentry:0468_auto__add_field_projectdebugfile_code_id__add_index_projectdebugfile_p > sentry:0468_recent_search > sentry:0469_fix_state > sentry:0470_org_saved_search > sentry:0471_global_saved_search_types > sentry:0472_auto__add_field_sentryapp_authorThe following content types are stale and need to be deleted:    sentry | dsymobject    sentry | dsymapp    sentry | useridentity    sentry | dsymsymbol    sentry | dsymsdk    sentry | globaldsymfile    sentry | versiondsymfile    sentry | projectdsymfile    sentry | grouphashtombstone    sentry | minidumpfile    sentry | dsymbundleAny objects related to these content types by a foreign key will alsobe deleted. Are you sure you want to delete these content types?If you're unsure, answer 'no'.    Type 'yes' to continue, or 'no' to cancel: yesRunning migrations for sentry.nodestore:- Nothing to migrate.Running migrations for sentry.search:- Nothing to migrate.Running migrations for social_auth:- Nothing to migrate.Running migrations for sentry.tagstore:- Nothing to migrate.Running migrations for sentry_plugins.hipchat_ac:- Nothing to migrate.Running migrations for sentry_plugins.jira_ac:- Nothing to migrate.Synced: > django.contrib.admin > django.contrib.auth > django.contrib.contenttypes > django.contrib.messages > django.contrib.sessions > django.contrib.sites > django.contrib.staticfiles > crispy_forms > debug_toolbar > rest_framework > sentry.plugins.sentry_interface_types > sentry.plugins.sentry_mail > sentry.plugins.sentry_urls > sentry.plugins.sentry_useragents > sentry.plugins.sentry_webhooks > sudo > southMigrated: - sentry - sentry.nodestore - sentry.search - social_auth - sentry.tagstore - sentry_plugins.hipchat_ac - sentry_plugins.jira_acCreating missing DSNsCorrecting Group.num_comments counterCleaning up...----------------You're all done! Run the following command get Sentry running:  docker-compose up -d


Всё, версия 9.1.2 установлена.

Выводы.

Это всем известные прописные истины. Не спешите, делайте резервные копии и проверяйте всё несколько раз.

Благодарю за внимание!
Подробнее..

Как восстановить Sentry после неудачного обновления

23.07.2020 00:16:58 | Автор: admin
Всем привет. Я хочу рассказать о том, как проходило восстановление Sentry после неудачного обновления.

Что же такое Sentry?

Это система полного отслеживания ошибок с открытым исходным кодом, которая поддерживает широкий спектр серверных, браузерных, настольных и родных мобильных языков и сред, включая PHP, Node. js, Python, Ruby, C #, Java, Go, React, Angular, Vue, JavaScript и другие.

Немного о том, как просто и непринуждённо всё поломалось.

Жили мы на версии 9.0.0 и пришло время обновиться. Пощупав web интерфейс 10.0.0, принял решение обновиться на неё. Это была самая большая ошибка!

Обновление прошло в штатном режиме. Сначала переход на 9.1.2 затем, на 10.0.0 (иначе нельзя). Далее обнаружил, что не запускается worker. Причиной этому добавление новых приложений и зависимостей.

Попытки собрать приложения и засунуть их в k8s не увенчались успехом.

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

После 3 часов работ и привлечения DBA эксперта данные были восстановлены до рабочего состояния.

Как?

Сделали рядом инстанс PG, закинули туда схему от версии 9.0.0 и начали потабличное восстановление. Конечно, без ошибок и напильника тут не обошлось.

На этом история не заканчивается. Пришло время всё же обновиться на версию 9.1.2.

Собрал образ, запускаю:

sentry upgrade

и видим вот такое чудесное сообщение:

 ! I'm not trusting myself; either fix this yourself by fiddling ! with the south_migrationhistory table, or pass --delete-ghost-migrations ! to South to have it delete ALL of these records (this may not be good).Exception in thread Thread-1 (most likely raised during interpreter shutdown)

После долгих поисков, проб и ошибок было найдено решение.

Необходимо восстановить схему данных текущей версии.

Для этого клонируем репозиторий:

git clone https://github.com/getsentry/onpremise.git

Переходим в ветку 9.1.2, так как ветки 9.0.0 уже нет.

cd onpremise && git checkout tags/9.1.2

Далее правим образ в Dockerfile:

#cat Dockerfile ARG SENTRY_IMAGEFROM ${SENTRY_IMAGE:-sentry:9.0.0}-onbuild
Затем правим compose file

docker-compose.yml
# NOTE: This docker-compose.yml is meant to be just an example of how# you could accomplish this on your own. It is not intended to work in# all use-cases and must be adapted to fit your needs. This is merely# a guideline.# See docs.getsentry.com/on-premise/server/ for full# instructionsversion: '3.4'x-defaults: &defaults  restart: unless-stopped  build:    context: .  depends_on:    - redis   # - postgres    - memcached    - smtp  env_file: .env  environment:    SENTRY_MEMCACHED_HOST: memcached    SENTRY_REDIS_HOST: redis    SENTRY_POSTGRES_HOST: 'sentry.cl.ats'    SENTRY_DB_USER: 'sentryDB'    SENTRY_DB_NAME: 'sentry_user'    SENTRY_DB_PASSWORD: 'sentry_passwd'    SENTRY_POSTGRES_PORT: 5432    SENTRY_EMAIL_HOST: smtp  volumes:    - sentry-data:/var/lib/sentry/filesservices:  smtp:    restart: unless-stopped    image: tianon/exim4  memcached:    restart: unless-stopped    image: memcached:1.5-alpine  redis:    restart: unless-stopped    image: redis:3.2-alpine  #postgres:  #  restart: unless-stopped  #  image: postgres:9.5  #  environment:  #    POSTGRES_HOST_AUTH_METHOD: 'trust'  #  volumes:  #    - sentry-postgres:/var/lib/postgresql/data  web:    <<: *defaults    ports:      - '9000:9000'  cron:    <<: *defaults    command: run cron  worker:    <<: *defaults    command: run workervolumes:    sentry-data:      external: true    #sentry-postgres:    #  external: true


Немного пояснений. Так как у меня есть отдельный инстанс базы данных, то создание через compose пропускаю, также не нужен volume sentry-postgres.

После данных манипуляций запускаем сборку:

./install.sh 

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

docker-compose run --rm web sentry django migrate --delete-ghost-migrations

Примерный вывод
Creating network "onpremise_default" with the default driverCreating onpremise_memcached_1 ... doneCreating onpremise_smtp_1      ... doneCreating onpremise_redis_1     ... done21:30:26 [INFO] sentry.plugins.github: apps-not-configuredRunning migrations for sentry:- Nothing to migrate. - Loading initial data for sentry.Installed 0 object(s) from 0 fixture(s)Running migrations for nodestore:- Nothing to migrate. - Loading initial data for nodestore.Installed 0 object(s) from 0 fixture(s)Running migrations for search:- Nothing to migrate. - Loading initial data for search.Installed 0 object(s) from 0 fixture(s)Running migrations for social_auth:- Nothing to migrate. - Loading initial data for social_auth.Installed 0 object(s) from 0 fixture(s)Running migrations for tagstore:- Nothing to migrate. - Loading initial data for tagstore.Installed 0 object(s) from 0 fixture(s)Running migrations for jira_ac:- Nothing to migrate. - Loading initial data for jira_ac.Installed 0 object(s) from 0 fixture(s)Running migrations for hipchat_ac:- Nothing to migrate. - Loading initial data for hipchat_ac.Installed 0 object(s) from 0 fixture(s)


Далее можно запускать upgrade.

docker-compose run --rm web sentry upgrade

Stdout
Starting onpremise_smtp_1      ... doneStarting onpremise_memcached_1 ... doneStarting onpremise_redis_1     ... done21:30:48 [INFO] sentry.plugins.github: apps-not-configuredSyncing...Creating tables ...Installing custom SQL ...Installing indexes ...Installed 0 object(s) from 0 fixture(s)Synced: > django.contrib.admin > django.contrib.auth > django.contrib.contenttypes > django.contrib.messages > django.contrib.sessions > django.contrib.sites > django.contrib.staticfiles > crispy_forms > debug_toolbar > raven.contrib.django.raven_compat > rest_framework > sentry.plugins.sentry_interface_types > sentry.plugins.sentry_mail > sentry.plugins.sentry_urls > sentry.plugins.sentry_useragents > sentry.plugins.sentry_webhooks > sudo > south > sentry_plugins.slackNot synced (use migrations): - sentry - sentry.nodestore - sentry.search - social_auth - sentry.tagstore - sentry_plugins.jira_ac - sentry_plugins.hipchat_ac(use ./manage.py migrate to migrate these)Running migrations for sentry:- Nothing to migrate. - Loading initial data for sentry.Installed 0 object(s) from 0 fixture(s)Running migrations for nodestore:- Nothing to migrate. - Loading initial data for nodestore.Installed 0 object(s) from 0 fixture(s)Running migrations for search:- Nothing to migrate. - Loading initial data for search.Installed 0 object(s) from 0 fixture(s)Running migrations for social_auth:- Nothing to migrate. - Loading initial data for social_auth.Installed 0 object(s) from 0 fixture(s)Running migrations for tagstore:- Nothing to migrate. - Loading initial data for tagstore.Installed 0 object(s) from 0 fixture(s)Running migrations for jira_ac:- Nothing to migrate. - Loading initial data for jira_ac.Installed 0 object(s) from 0 fixture(s)Running migrations for hipchat_ac:- Nothing to migrate. - Loading initial data for hipchat_ac.Installed 0 object(s) from 0 fixture(s)Creating missing DSNsCorrecting Group.num_comments counter


Итак, схема восстановлена. Можно обновиться на 9.1.2.

Для этого в Dockerfile меняем версию на 9.1.2:

ARG SENTRY_IMAGEFROM ${SENTRY_IMAGE:-sentry:9.1.2}-onbuild
И запускаем install.sh

./install.sh

Если всё сделано верно, то процесс завершится без ошибок. Вывод должен быть примерно такой:

Stdout
# ./install.sh Checking minimum requirements...Creating volumes for persistent storage...Created sentry-data..env already exists, skipped creation.Building and tagging Docker images...smtp uses an image, skippingmemcached uses an image, skippingredis uses an image, skippingBuilding webStep 1/2 : ARG SENTRY_IMAGEStep 2/2 : FROM ${SENTRY_IMAGE:-sentry:9.1.2}-onbuild# Executing 4 build triggers ---> Running in 6c97f9fcaf63DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-supportRemoving intermediate container 6c97f9fcaf63 ---> Running in 9e0f65ee3af6Removing intermediate container 9e0f65ee3af6 ---> Running in 09754c44319cRemoving intermediate container 09754c44319c ---> a12fa31c2675Successfully built a12fa31c2675Successfully tagged onpremise_web:latestBuilding cronStep 1/2 : ARG SENTRY_IMAGEStep 2/2 : FROM ${SENTRY_IMAGE:-sentry:9.1.2}-onbuild# Executing 4 build triggers ---> Using cache ---> Using cache ---> Using cache ---> Using cache ---> a12fa31c2675Successfully built a12fa31c2675Successfully tagged onpremise_cron:latestBuilding workerStep 1/2 : ARG SENTRY_IMAGEStep 2/2 : FROM ${SENTRY_IMAGE:-sentry:9.1.2}-onbuild# Executing 4 build triggers ---> Using cache ---> Using cache ---> Using cache ---> Using cache ---> a12fa31c2675Successfully built a12fa31c2675Successfully tagged onpremise_worker:latestDocker images built.Generating secret key...Secret key written to .envSetting up database...Starting onpremise_smtp_1      ... doneStarting onpremise_redis_1     ... doneStarting onpremise_memcached_1 ... done21:35:07 [WARNING] sentry.utils.geo: settings.GEOIP_PATH_MMDB not configured.21:35:10 [INFO] sentry.plugins.github: apps-not-configuredSyncing...Creating tables ...Installing custom SQL ...Installing indexes ...Installed 0 object(s) from 0 fixture(s)Migrating...Running migrations for sentry: - Migrating forwards to 0472_auto__add_field_sentryapp_author. > sentry:0424_auto__add_field_integration_status > sentry:0425_auto__add_index_pullrequest_organization_id_merge_commit_sha > sentry:0425_remove_invalid_github_idps > sentry:0426_auto__add_sentryappinstallation__add_sentryapp__add_field_user_is_sent > sentry:0427_auto__add_eventattachment__add_unique_eventattachment_project_id_event > sentry:0428_auto__add_index_eventattachment_project_id_date_added > sentry:0429_auto__add_integrationexternalproject__add_unique_integrationexternalpr > sentry:0430_auto__add_field_organizationintegration_status > sentry:0431_auto__add_field_externalissue_metadata > sentry:0432_auto__add_field_relay_is_internal > sentry:0432_auto__add_index_userreport_date_added__add_index_eventattachment_date_ > sentry:0433_auto__add_field_relay_is_internal__add_field_userip_country_code__add_ > sentry:0434_auto__add_discoversavedqueryproject__add_unique_discoversavedqueryproj > sentry:0435_auto__add_field_discoversavedquery_created_by > sentry:0436_rename_projectdsymfile_to_projectdebugfile > sentry:0437_auto__add_field_sentryapp_status > sentry:0438_auto__add_index_sentryapp_status__chg_field_sentryapp_proxy_user__chg_ > sentry:0439_auto__chg_field_sentryapp_owner > sentry:0440_auto__del_unique_projectdebugfile_project_debug_id__add_index_projectd > sentry:0441_auto__add_field_projectdebugfile_data > sentry:0442_auto__add_projectcficachefile__add_unique_projectcficachefile_project_ > sentry:0443_auto__add_field_organizationmember_token_expires_at > sentry:0443_auto__del_dsymapp__del_unique_dsymapp_project_platform_app_id__del_ver > sentry:0444_auto__add_sentryappavatar__add_field_sentryapp_redirect_url__add_field > sentry:0445_auto__add_promptsactivity__add_unique_promptsactivity_user_feature_org > sentry:0446_auto__add_index_promptsactivity_project_id > sentry:0447_auto__del_field_promptsactivity_organization__add_field_promptsactivit > sentry:0448_auto__add_field_sentryapp_is_alertable > sentry:0449_auto__chg_field_release_owner > sentry:0450_auto__del_grouphashtombstone__del_unique_grouphashtombstone_project_ha > sentry:0451_auto__del_field_projectbookmark_project_id__add_field_projectbookmark_ > sentry:0452_auto__add_field_sentryapp_events > sentry:0452_auto__del_field_releaseenvironment_organization_id__del_field_releasee > sentry:0453_auto__add_index_releasefile_release_name > sentry:0454_resolve_duplicate_0452 > sentry:0455_auto__add_field_groupenvironment_first_seen > sentry:0456_auto__add_dashboard__add_unique_dashboard_organization_title__add_widg > sentry:0457_auto__add_field_savedsearch_is_global__chg_field_savedsearch_project__ > sentry:0457_auto__add_monitorcheckin__add_monitor__add_index_monitor_type_next_che > sentry:0458_global_searches_data_migrationSaved Searchs: 100% |########################################################################################################################################################################| Time: 0:00:07 > sentry:0459_global_searches_unique_constraint > sentry:0460_auto__add_field_servicehook_organization_id > sentry:0461_event_attachment_indexes > sentry:0462_auto__add_servicehookproject > sentry:0462_releaseenvironment_project_id > sentry:0463_backfill_service_hook_project > sentry:0464_auto__add_sentryappcomponent__add_field_sentryapp_schema > sentry:0464_groupenvironment_foreignkeys > sentry:0465_sync > sentry:0466_auto__add_platformexternalissue__add_unique_platformexternalissue_grou > sentry:0467_backfill_integration_status > sentry:0468_auto__add_field_projectdebugfile_code_id__add_index_projectdebugfile_p > sentry:0468_recent_search > sentry:0469_fix_state > sentry:0470_org_saved_search > sentry:0471_global_saved_search_types > sentry:0472_auto__add_field_sentryapp_authorThe following content types are stale and need to be deleted:    sentry | dsymobject    sentry | dsymapp    sentry | useridentity    sentry | dsymsymbol    sentry | dsymsdk    sentry | globaldsymfile    sentry | versiondsymfile    sentry | projectdsymfile    sentry | grouphashtombstone    sentry | minidumpfile    sentry | dsymbundleAny objects related to these content types by a foreign key will alsobe deleted. Are you sure you want to delete these content types?If you're unsure, answer 'no'.    Type 'yes' to continue, or 'no' to cancel: yesRunning migrations for sentry.nodestore:- Nothing to migrate.Running migrations for sentry.search:- Nothing to migrate.Running migrations for social_auth:- Nothing to migrate.Running migrations for sentry.tagstore:- Nothing to migrate.Running migrations for sentry_plugins.hipchat_ac:- Nothing to migrate.Running migrations for sentry_plugins.jira_ac:- Nothing to migrate.Synced: > django.contrib.admin > django.contrib.auth > django.contrib.contenttypes > django.contrib.messages > django.contrib.sessions > django.contrib.sites > django.contrib.staticfiles > crispy_forms > debug_toolbar > rest_framework > sentry.plugins.sentry_interface_types > sentry.plugins.sentry_mail > sentry.plugins.sentry_urls > sentry.plugins.sentry_useragents > sentry.plugins.sentry_webhooks > sudo > southMigrated: - sentry - sentry.nodestore - sentry.search - social_auth - sentry.tagstore - sentry_plugins.hipchat_ac - sentry_plugins.jira_acCreating missing DSNsCorrecting Group.num_comments counterCleaning up...----------------You're all done! Run the following command get Sentry running:  docker-compose up -d


Всё, версия 9.1.2 установлена.

Выводы.

Это всем известные прописные истины. Не спешите, делайте резервные копии и проверяйте всё несколько раз.

Благодарю за внимание!
Подробнее..

Почему я не буду пользоваться онлайн-кинотеатрами

13.07.2020 10:06:40 | Автор: admin
Всем привет! Меня зовут Леонид, я разработчик сайта Поиск VPS. Некоторое время назад ко мне в обратную связь писало немало пользователей, которые просили помощи в подборе виртуального сервера для организации онлайн-кинотеатра. Естественно, планировалось использовать VPS только под сайт без хранения контента, но набор требований был всегда примерно одинаковый: абузоустойчивость, оплата биткоинами, круглосуточная поддержка и размещение не в России. В последнее время число таких запросов снизилось почти до нуля, как мне кажется потому, что большую часть рынка поделили между собой легальные онлайн-кинотеатры. У меня оформлено некоторое количество подписок, и на мой взгляд у таких сервисов есть некоторые существенные недостатки, о которых я сегодня хочу рассказать.


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

Цены на онлайн-кинотеатры в России примерно сопоставимы с ценами в США или Европе, хотя средний доход сильно меньше. Для среднестатистического американца подписка за 10 долларов не нанесет никакого удара по бюджету, в то время как для российского пользователя такая цена будет уже более ощутимой.

Невозможно купить одну подписку и смотреть любые фильмы и сериалы

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


Сервисы предлагают докупить контент, который не входит в подписку



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


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

Очень часто подписка ограничена страной или регионом

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


Часть контента невозможно заранее загрузить на устройство

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

Уже загруженный контент нужно периодически обновлять

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

Не всегда можно выбрать качество картинки при просмотре онлайн

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

Из проката могут пропадать отдельные сезоны или целые сериалы

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

Правообладатели могут ставить любые, даже самые нелепые условия


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


Увидев ее я не поверил, что такое может быть, но техподдержка развеяла мои сомнения, и все оказалось правдой: после выхода серии ее можно посмотреть только в течении 35 дней. Не успел твои проблемы. В моем случае получилось, что предыдущий сезон я закончил смотреть в тот момент, когда первые 4 серии нового сезона уже не были доступны.


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

Отсутствие субтитров и оригинальных звуковых дорожек

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


Перевод новых серий сериалов может появляться с очень большой задержкой

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


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

Что на мой взгляд можно улучшить:

  1. Предлагать короткий кусок видео, чтобы понять, какая озвучка и качество видео.
  2. Если пользователь купил просмотр на 48 часов (например, у Кинопоиска), то предлагать после просмотра доплатить разницу до версии, которую можно смотреть неограниченное число раз. Аналогично у Окко можно купить просмотр с низким качеством: было бы круто, если бы можно было доплатить разницу.
  3. Сделать возможность покупки одного просмотра фильма без ограничения по времени, то есть хоть по 10 минут в день, но посмотреть можно только 1 раз, естественно, по сниженной стоимости.
Подробнее..

Из песочницы Анализ результатов нагрузочного тестирования

08.08.2020 00:06:58 | Автор: admin
С каждым днем в мире становится все больше и больше инструментов для проведения нагрузочного тестирования. Собственно, и сам интерес к этой теме начинает возрастать.
Основная задача инструмента нагрузочного тестирования подать заданную нагрузку на систему. Но кроме этого есть еще одна, не менее важная задача предоставить отчет о результатах подачи этой нагрузки. Иначе мы проведем тестирование, но ничего не сможем сказать о его результате и не сможем достаточно точно определить, с какого момента началась деградация системы.

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



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

Мы в Тинькофф активно используем инструмент Gatling, поэтому на его примере расскажем, как создать отчет вашей мечты и куда следует смотреть при его анализе. Также сразу хочу заметить, что почти все графики, описанные в статье, можно получать в режиме онлайн с помощью связки вашего инструмента с Grafana. Это наиболее удобный инструмент для создания отчетов на лету, с помощью сконфигурированного заранее дашборда. Более того, это позволяет более быстро создать почти любой график на основе отправленных вами данных. Уже сейчас есть готовые дашборды почти для всех инструментов нагрузочного тестирования. Графики для других инструментов MF LoadRunner и Apache JMeter тоже будут приведены, их анализ строится по аналогии с Gatling.

Основные метрики


Индикаторы


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

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

  • отличная время отклика менее 50 секунд;
  • средняя более 50, но менее 100 секунд;
  • ужасная более 100 секунд.

В Gatling вы сами можете настроить пороги для перехода из группы в группу и их количество в файле gatling.conf. Графики такого типа лучше строить на основе методики. APDEX (Application Performance Index)
Также можно добавить индикатор с количеством/процентами ошибочных запросов.



Метод APDEX позволяет использовать индикаторы в регрессионном тестировании для сравнения релизов: так сразу видно, насколько хуже или лучше стал релиз в общем. К сожалению, этого графика нет из коробки в MF LoadRunner и Apache JMeter, но его легко создать с помощью дашборда Grafana.

Таблица с временем отклика


По умолчанию Gatling строит таблицу по перцентилям, среднему и максимальному времени отклика, а также по ошибкам. По ней отслеживается выход за пределы SLA (превышение нефункциональных требований). Обычно в SLA указывают перцентили 95, 99 и процент ошибок. Таким образом таблица позволяет получить быструю оценку результатов тестирования.

Если группировать запросы в виде транзакций, можно увидеть в таблице как оценку отдельных запросов, так и оценку всей группы и транзакции сразу.
HTML Gatling Report
MF LoadRunner также создает таблицу сам в блоке Analysis Summary Report и называется Transaction Summary
В Apache JMeter эти данные можно найти в Aggregate Report

График Virtual Users


Обычно измеряется в штуках и показывает, как пользователи заходят в приложение, тем самым иллюстрируя реальный профиль нагрузки. Стоит сразу оговориться, что для MF LoadRunner и Gatling эти графики показывают количество Virtual Users, а для Apache JMeter количество Thread.

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

  • Active Users отображает, сколько потоков сейчас активно в секунду. Когда потоки стартуют и останавливаются, особенно в открытой модели нагрузки, этот показатель будет колебаться на протяжении всего теста.
  • Total VUsers показывает, сколько потоков стартовали и останавливались с начала тестирования суммарно. Удобно для закрытой модели нагрузки, в которой потоки не умирают.

Вид графика также зависит от модели нагрузки:

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

HTML Gatling Report
В MF LoadRunner этот график называется Running Vusers
В Apache JMeter график называется Active Threads Over Time и не входит в стандартную поставку, но его можно бесплатно скачать на сайте JMeter-Plugins.org

График Response Time


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

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

Кроме графика времени ответа каждого запроса удобно показывать также линию с суммарным временем ответа (Total Response Time). Если наложить график подаваемой нагрузки (VU/RPS), можно отслеживать корреляцию с увеличением времени отклика от увеличения подаваемой нагрузки (VU/RPS). В Apache JMeter этот график называется Response Times vs Threads.

Далее пойдут графики, на которых может быть множество линий, каждая из которых отображает свой сценарий или запрос. Если у вас сложный тест со множеством операций и нелинейным профилем, советуем отражать в отчете лишь наиболее показательные запросы или группы запросов. Как вариант, можно отражать лишь запросы, превысившие SLA/SLO, чтобы не засорять график и отчет.
В MF LoadRunner график называется Average Transaction Response Time и показывает среднее время для транзакций
Для Apache JMeter график существует в двух вариантах в расширенном пакете с сайта JMeter-Plugins.org и называется Response Times Over Time и, по умолчанию, Response Time Graph. Более наглядный и удобный, на мой взгляд, первый вариант


Вариации графика


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

В тестировании производительности чаще всего используется 95 и 99 перцентиль для большей наглядности. Однако, если вы все же смотрите среднее время отклика, вам стоит учитывать при этом стандартное (среднеквадратичное) отклонение.
HTML Gatling Report
Для MF LoadRunner график будет называться Transaction Response Time (Percentile)
Также вы можете получить и перцентили для Apache JMeter с помощью графика Response Times Percentiles из этого же расширенного набора

Распределение Response Time


Также есть прекрасные графики, показывающие зависимость распределения времени от количества запросов.

Этот вид графиков чем-то напоминает индикаторы, но показывает более полную картину распределения времени, без обрезки перцентилями или другими агрегатами. С помощью графика можно более наглядно определить границы для групп индикаторов. У MF LoadRunner такого графика нет.
HTML Gatling Report для каждого запроса
Есть еще один вариант распределения времени выполнения запроса от количества запросов в разрезе успешных и ошибочных запросов для всего теста
Для MF LoadRunner данный график будет называться Transation Response Time (Distribution) и показывать распределение для транзакций
В Apache JMeter тоже есть этот график в расширенном пакете и называется Response Times Distribution

Latency


Из этой метрики также можно выделить дополнительный параметр Latency (миллисекунды) время задержки (чаще всего под этим понимают Network Latency). Этот параметр показывает время между окончанием отправки запроса до получения первого ответного пакета от системы.
С помощью этого параметра можно измерять также задержки на сетевом уровне, если параметр будет расти. Желательно, чтобы он стремился к нулю. Этот и следующий тип графиков в основном используются при глубоком анализе и поиске проблем производительности. Этого графика из коробки нет в Gatling. В MF LoadRunner этот график называется Average Latency Graph в блоке Network Virtualization Graphs если у вас установленные агенты мониторинга.
В Apache JMeter этот график присутствует лишь в расширенном наборе и называется Response Latencies Over Time

Bandwidth


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

Изменяя этот параметр на инструменте нагрузки можно моделировать различные источники подключений к приложению: мобильная связь 4G или проводная сеть. В бесплатной версии Gatling этого графика нет, он есть лишь в платной версии Gatling FrontLine. Этот график из коробки есть лишь в MF LoadRunner, находится в том же блоке, что и Latency, и называется Average Bandwidth Utilization Graph.

График Request Per Second


Измеряется в штуках в секунду показывает количество запросов, поступающее в систему за 1 секунду.

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

  1. Если наложить график на VU, можно увидеть увлечение RPS/TPS с увлечением количества пользователей, а также уменьшение в связи с выходом пользователей или стабилизацией подаваемой нагрузки.
  2. Если наложить график Response Time, можно увидеть среднее время, за которое обрабатываются все транзакции или запросы на протяжении теста.

HTML Gatling Report
В MF LoadRunner график называется Hits per Second
Для Apache JMeter существует график Hits per Second из расширенного пакета

TPS


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

Например, транзакция вход в личный кабинет включает следующие запросы: открытие главной страницы, ввод логина, пароля, нажатие кнопки отправить, переадресацию на приветственную страницу в единицу времени. В Gatling график можно получить лишь с помощью применения Grafana, так как для групп в HTML-отчёте строятся графики лишь по времени отклика.
MF LoadRunner Transactions per Second
Для расширенного пакета Apache JMeter Transactions per Second

График Errors


Обычно измеряется в rate (штук в секунду) график показывает рост количества ошибочных запросов. Также удобно измерять значение в процентах от общего числа запросов. По этому графику отслеживается выход за пределы SLA по количеству или проценту ошибок.

Если наложить график Response Time, можно увидеть, как увеличение ошибок влияет на рост времени ответа приложения.

Gatling по умолчанию не имеет отдельного графика, отображающего лишь ошибки. В Gatling он совмещен с графиком VU и сразу показывает, как рост нагрузки сказывается на росте числа ошибок, и помогает обнаружить порог, с которого идет превышение SLA или вообще появляются ошибки. В Apache JMeter также нет отдельного графика, он совмещен с графиками количества транзакций.
HTML Gatling Report
В MF LoadRunner этот график называется Errors per Second

HTTP responses status


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

Например, если на предыдущем графике вы получили 100%, вы начинаете анализировать, а какие именно 50x ошибки из-за того, что сервер не отвечает, или 403 из-за того, что неверный пул и пользователи не могу авторизоваться, если, например, вы используете HTTP-протокол.
Изначально в бесплатной версии Gatling этого графика нет, он есть лишь в платной версии Gatling FrontLine. Чтобы график появился в бесплатной версии, необходимо перенастроить logback.xml так, чтобы логи собирались в graylog, и уже в нем строить нужный график.
В MF LoadRunner этот график называется HTTP Responses per Second
В Apache JMeter этот график называется Response Codes per Second из расширенного пакета

График Throughput


Измеряется обычно в битах в секунду. График показывает пропускную способность приложения, а именно какой объем данных был отправлен и обработан приложением в единицу времени.
Обычно его используют для глубокого анализа проблемы приложений. В Gatling этот график содержится лишь в FrontLine, в бесплатной версии его нет.
Этот график есть из коробки в MF LoadRunner, он называется Throughput
В Apache JMeter график называется Bytes Throughput Over Time из расширенного пакета

Возможные модификации


  1. Если наложить график Response Time, можно увидеть, что рост времени ответа часто связан с увеличением числа отправленных данных (Throughput). Если на графике замечен рост Response Time, а Throughput при этом остается прежним, это указывает на проблемы с сетью или с приложением: где-то начинает копиться очередь запросов.
  2. Если наложить график Bandwidth, то при совпадении объема данных на двух графиках будет видно, что ограничивающим фактором является пропускная способность сети.
  3. Если наложить график VU, то они должны полностью коррелировать, без резких скачков и провалов, так как рост нагрузки должен приводить к планомерному росту объема данных. Расхождения или резкие выбросы являются поводами для дальнейшего изучения.

Получение графиков


Большинство графиков можно получить, используя отчет HTML Based Gatling Reports после теста или же настроив связку мониторинга Graphite-InfluxDB-Grafana. Для отображения можно использовать готовый дашборд из библиотеки дашбордов https://grafana.com/grafana/dashboards/9935.

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

Матричное описание метрик


Всё, что мы описали выше, представлено в виде маленькой таблички, суммирующей все эти знания.
Тип VU Response Time Requests Errors Throughput
VU Позволяет сравнить планируемую нагрузку с реальной Показывает, как увеличение пользователей сказывается на росте времени отклика при закрытой модели Можно увидеть увеличение RPS/TPS/HITS с увеличением количества пользователей, а также уменьшение в связи с выходом пользователей или стабилизацией подаваемой нагрузки Показывает, при каком числе VU происходит рост ошибок. Не всегда показателен, так как в открытой модели сложно коррелировать графики Метрики должны полностью коррелировать, так как рост пользователей приводит к росту объема данных, отправляемых ими. Расхождения или резкие выбросы являются поводами для дальнейшего изучения
Response Time Показывает, как быстро сервер отвечает на наши запросы. Основная метрика Показывает среднее время, за которое обрабатываются все транзакции или запросы на всем протяжении теста Можно увидеть, как увеличение ошибок влияет на рост времени ответа приложения Рост времени ответа часто связан с увеличением количества отправленных данных (Throughput). Если на графике замечен рост Response Time, а Throughput при этом остается прежним, это указывает на проблемы с сетью или с приложением: где-то начинает копиться очередь запросов
Requests Показывает, сколько запросов выдерживает система в секунду, минуту и т. д. Позволяет найти значение интенсивности, после которой появляются ошибки. Удобно для определения SLA Позволяет контролировать рост объема данных и количества запросов. Эти метрики должны коррелировать. Появление проблем может свидетельствовать, что мы отправляем неверные запросы
Errors Позволяет оценить рост числа ошибок и тип ошибок, которые проявляются под нагрузкой Можно найти объем данных, при котором приложение начинает генерировать ошибки
Throughput Показывает, какой объем данных приложение может прокачать через себя за заданный промежуток времени
Подробнее..

Прозрачные процессы тестирования на удалёнке

15.07.2020 18:16:18 | Автор: admin

Публикуем статью Анастасии Шариковой QA Lead в Bookmate и преподавателя профессионального курса QA Lead, с программой которого мы приглашаем вас ознакомиться!


Также приглашаем на бесплатный пробный открытый урок Тестовое покрытие по Бейзеру, где Анастасия Асеева-Нгуен (эксперт по инженерным практикам в Tinkoff Group) рассмотрит основные подходы для построения тестовой модели, расскажет, что такое test coverage и code coverage, покажет способы подсчета тестового покрытия, а также подробно раскроет темы: цикломатическая сложность, использование статических анализаторов для расчета тестового покрытия, диаграммы для подсчета тестового покрытия.




Всем привет!
Меня зовут Анастасия Шарикова, я руковожу отделом тестирования в Bookmate и веду Telegram-канал Yet another QA.


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


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


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


Внятное рабочее время


Организуйте внятное понимание рабочего времени! Соблазн работать с утра до ночи в половину силы вместо нормальных рабочих 8 часов велик, да и вероятность выгорания от работы по 12 активных часов тоже. Простые практики, которые помогут всем:


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

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


Старайтесь больше общаться в мессенджерах


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


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

Не забывайте про видео и аудио


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


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


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


А еще это прекрасный шанс наконец-то попробовать возможности таких инструментов, как, например, Miro, Notion или Trello для визуализации и совместной работы. Например, для работы над большой задачей или для создания чек-листов для командной работы.


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


Внедряйте демо в процессы


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


Сделайте более прозрачным доступ к устройствам


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


Ищите баланс


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


И в качестве вывода


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


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




Узнать подробнее о курсе QA Lead



Подробнее..

Перевод Тестирование назад к основам PuppeteerMocha Совершенствуйте код с помощью тестового покрытия

09.07.2020 20:08:37 | Автор: admin

Перевод статьи подготовлен в преддверии старта курса Автоматизация тестирования на JavaScript.




Меня несколько раз спрашивали о разнице между инженером по обеспечению качества (QA Quality Assurance) и тестером (QC Quality Control), и я понял, что даже если люди, разрабатывающие программное обеспечение, путают такие простые понятия, то что уже говорить про остальных? Но по большому счету, вы понимаете, что им все равно, и это их право!


Людям просто нужны качественные продукты! Менеджеры хотят иметь программное обеспечение без багов [1] наилучшего качества, пользователям нужны качественные приложения, а этого можно достигнуть путем неустанного тестирования [2]. Но что это значит? Что такое качество (Quality)? Все дело в удовлетворении настолько тривиально! Мера качества это счастье конечных пользователей! Достижение этой абстракции становится настоящим испытанием для команды [3].


При разработке любого продукта, особенно программного обеспечения без тестирования, мы сталкиваемся с рисками [4], а они, с большой долей вероятности, сопряжены с потерей средств [5]. Так что, если вы не большой фанат тестирования, но для вас важно качество, то я предлагаю вам подумать еще раз и дать процессам обеспечения качества делать свое дело. Это относится ко всем членам команды и особенно к инженерам, которые имеют отношение к тест дизайну [6] инженерам по обеспечению качества.


Таким образом, посыл, что человек должен быть удовлетворен качественным приложением, подкрепляется подходами, уже сформировавшимися в лучшие практики [7]. Однажды занявшись тестированием, вы узнаете о существовании различных типов тестирования [8] и документации по тестированию (например, план тестирования). Затем вы сможете обрабатывать тест кейсы [9] на основе разных техник тест дизайна [10]. В конце концов, вы откроете для себя автоматизацию и к тому времени поймете разницу между QA и QC [11].


Глоссарий:


  1. несоответствие между фактическим и ожидаемым поведением
  2. полной проверки продукта путем верификации, валидации на основе спецификаций для обнаружения проблем
  3. люди, которые разрабатывают приложения, такие как дизайнеры, программисты, тестировщики
  4. (а) возможность негативного исхода (в соответствии с Кембриджским словарем);
    (b) действия в надежде на позитивных исход (в соответствии со словарем Google)
  5. (a) 125 миллионов пробников почвы Марса, потерянных из-за простой математической ошибки
    (b) много человеческих ошибок, приведших к крушению Boeing 737 Max
  6. пирамида тестов, фреймворк для разработки набора тестов, который оптимизирует скорость выполнения тестов с уверенностью, что ваше приложение работает должным образом
  7. семь принципов тестирования на International Software Testing Qualifications Board
  8. различные типы тестирования программного обеспечения от Atlassian
  9. этапы воспроизведения с ожидаемыми результатами в соответствии с требованиями приложения
  10. общие техники тестирования, которые вы должны знать
  11. ищите ответ здесь

Книги:


  • Тестирование Дот Ком, или Пособие по жестокому обращению с багами в интернет-стартапах, Савин Р. 2007
  • Continuous delivery. Практика непрерывных апдейтов, Эберхард В. 2017
  • Software Testing Automation Tips, Алпаев Г. 2017

[Puppeteer][Mocha] Проапгрейдите свой код с помощью тестового покрытия.


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


С момента релиза Puppeteer сквозное тестирование (end-2-end) стало быстрым и надежным способом тестирования функционала. Большинство вещей, которые вы можете сделать вручную в браузере, можно сделать с помощью Puppeteer. Более того, Chrome без графики снижает нагрузку на производительность, а нативный доступ к протоколу DevTools делает Puppeteer просто потрясающим инструментом. Представьте, что каждый раз, когда мы разрабатываем интерфейс, мы просто проверяем окончательный вид в браузере без TDD мы сталкиваемся с мороженкой-антипатерном пирамиды тестов.



Сверху вниз: Ручные тесты / Автоматизированные GUI-тесты / Интеграционные тесты / Юнит-тесты


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


Настройка


У меня есть полная пошаговая README.md инструкция, основанная на простом проекте, который я форкнул и снабдил его многофункциональным тестовым апгрейдом в целях демонстрации (или чтобы выпендриться). Итак, если у вас есть еще один, пожалуйста:


1) Установите зависимости в корне


npm i puppeteer mocha puppeteer-to-istanbul nyc -D

2) Предоставьте свой экземпляр на конечной точке (мое простое решение для *.html http-сервер)


3) Создайте каталог test и заполните {yourFeature}_test.js следующим подходящим шаблоном (заполните хуки before и after), попробуйте расширить его с помощью специфичных для проекта селекторов и поведений:


const puppeteer = require('puppeteer');const pti = require('puppeteer-to-istanbul');const assert = require('assert');/** * ./test/script_test.js * @name Feature testing * @desc Create Chrome instance and interact with page. */let browser;let page;describe('Feature one...', () => {    before(async () => {        // Создание экземпляра браузера        browser = await puppeteer.launch()        page = await browser.newPage()        await page.setViewport({ width: 1280, height: 800 });        // Подключите покрытие JavaScript и CSS        await Promise.all([            page.coverage.startJSCoverage(),            page.coverage.startCSSCoverage()          ]);        // Конечная точка для эмуляции изолированного окружения        await page.goto('http://localhost:8080', { waitUntil: 'networkidle2' });    });    // Первый тестовый набор    describe('Visual regress', () => {        it('title contain `Some Title`', async () => {            // Настройка            let expected = 'Some Title';            // Выполнение            let title = await page.title();            // Проверка            assert.equal(title, expected);        }).timeout(50000);    });    // Второй тестовый набор    describe('E2E testing', () => {        it('Some button clickable', async () => {            // Настройка            let expected = true;            let expectedCssLocator = '#someIdSelector';            let actual;            // Выполнение            let actualPromise = await page.waitForSelector(expectedCssLocator);            if (actualPromise != null) {                await page.click(expectedCssLocator);                actual = true;            }            else                actual = false;            // Проверка            assert.equal(actual, expected);        }).timeout(50000);    // Сохранить покрытие и закрыть контекст браузера    after(async () => {        // Отключить покрытие JavaScript и CSS        const jsCoverage = await page.coverage.stopJSCoverage();        await page.coverage.stopCSSCoverage();        let totalBytes = 0;        let usedBytes = 0;        const coverage = [...jsCoverage];        for (const entry of coverage) {            totalBytes += entry.text.length;            console.log(`js fileName covered: ${entry.url}`);            for (const range of entry.ranges)                usedBytes += range.end - range.start - 1;        }        // вывести в лог исходное байтовое покрытие        console.log(`Bytes used: ${usedBytes / totalBytes * 100}%`);        pti.write(jsCoverage);        // Закрыть экземпляр браузера        await browser.close();    });});

Выполнение


  1. Запустите описанный выше тест для сценариев в конечной точке с помощью команды mocha
  2. Получите покрытие, собранное при выполнении теста с nyc report.
  3. Я предлагаю вам использовать ваш package.json с помощью следующих сценариев, что делает его очень простым для выполнения задач, таких как npm test или npm run cover

 "scripts": {    "pretest": "rm -rf coverage && rm -rf .nyc_output",    "test": "mocha --timeout 5000 test/**/*_test.js",    "server": "http-server ./public",    "coverage": "nyc report --reporter=html"  },

Покрытие


Мой проект покрыт на приблизительно 62%



Мы можем получить отчет в виде html и посмотреть поближе.



Вы можете видеть, что Branches и Functions покрыты на 100%. В то время как я тестировал функцию покрытия Puppeteer (типа Coverage devTool) я создал этот багрепорт.



[Bug] Неверные ветви попадают в статистику покрытия #22
Размещено storenth 27 декабря 2018
Когда готов nyc report --reporter=htm, я пытался посмотреть на ./coverage/index.html и обнаружил большой дефект в числе покрытия Branch, оно всегда равно 100%. Чтобы изучить эту проблему, я предлагаю клонировать этот простой репозиторий для локального воспроизведения.


Холивар Unit против E2E


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


Внесите свой вклад


Я настоятельно рекомендую уделить некоторое время и ознакомиться с моим github-working-draft проектом прежде, чем вы наломаете дров. Я буду признателен за любое сотрудничество и обратную связь. Не стесняйтесь обращаться ко мне с любыми вопросами.




Узнать подробнее о курсе Автоматизация тестирования на JavaScript



Подробнее..

Как найти границы на клиенте и сервере

10.07.2020 16:05:35 | Автор: admin
Как обычно тестировщик ищет границы в поле? Если в ТЗ есть ограничения, то тестирует их. А если их нет? С нижней границей все понятно это пустое поле. А как найти верхнюю? Вставляем большую строку и смотрим, сколько символов сохранится. И всё

Но если у нас клиент-серверное приложение, то границы разработчик может поставить на каждом звене!



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

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


Содержание





1. Границы на клиенте



Maxlength


Ограничение по длине строки на клиенте прописывают в параметре maxlength поля.
Чтобы его найти, вам нужно:

  1. Открыть панель разработчика нажать f12.
  2. Нажать самую левую кнопку и навести курсор на элемент на странице.

Вуаля! Для поля имя1 у нас стоит ограничение в 10 символов.



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

Проверяем ограничение пытаемся ввести больше 10 символов. Но вводится ровно 10, больше не дает. Печатаешь на клавиатуре, а система просто не реагирует:



Это граница! Граница на клиенте, мы ее нашли, ура :)

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

Поэтому мы границу снимаем. Для этого прямо в DOM-модели (это структура нашей страницы, по которой мы искали инспектором) исправляем строчку с параметром. Да, оно меняется. Дважды кликаем на параметр maxlength и меняем значение. Вуаля! Теперь можно ввести намного больше символов, чем раньше!



Обратите внимание символы можно вводить сразу, без обновления страницы. Более того, если вы обновите страницу, то ваши изменения пропадут. Ведь при обновлении страницы HTML-код запрашивается с сервера, а там ваших изменений нет. Так что балуйтесь сколько хотите, не бойтесь что-то сломать =)

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



Найдя элемент в инспекторе, вы можете увидеть и другие цифры, кроме maxlength. Например, data-max или data-jsmax, или какие-то еще. Можно ли считать их границами? Только если вы умеете читать код и найдете в нем их значение.

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

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

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

А всё остальное надо проверять, ограничивает нас как-то, или нет. Как проверять? Включить консоль!


Ошибки в консоли JS


При тестировании веба нужно обязательно включить консоль:

F12 Консоль



И следить за ней краем глаза. А иначе можно пропустить ошибку!

Бывает, что система никак не проявляет проблему в самом интерфейсе ничего не меняется. Вводим данные в поля? Ничего красным не подсвечивается, но в консоли появляется ошибка. Тыкаем на кнопку? Загружается отчет, вроде всё нормально, но в консоли ошибка.

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

Итак, открываем консоль и начинаем заполнять поля пользователя. Вот, например, вбиваем данные в поле хомячок. Сняли границу на клиенте (maxlength) и печатаем. И тут замечаем, что в консоли появилась ошибка!



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

В случае с Users разработчик ничего не путал, у него по ТЗ стояло выводить такое сообщение в консоль =)) Потому что наша задача была просто показать ситуацию, когда в интерфейсе все хорошо, а в консоли есть ошибки.

Но где граница? Судя по сообщению об ошибке Maximum 10. Значит, граница 10 символов. Да? Ни фига! Граница это когда у нас до нее поведение системы одно (ошибок нет), а после другое.



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

Начинаем вводить символы медленно, следя за консолью:

  • 10 нет ошибки
  • 11 нет ошибки
  • 12 ошибочка!

Ага, значит, граница по JS у нас не 10, а 11 символов! До 11 все хорошо, а после сыпятся ошибки. А сообщение об ошибке, как оказалось, врет. Так что доверяй, но проверяй =)

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

  1. maxlength = 10 символов
  2. js = 11 символов

А вот в поле имя 1 одна граница на клиенте: maxlength = 10 символов. Ошибок в консоли при вводе символов там нет.


Изменение поведения


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

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


В Users таких границ нет, поэтому я просто взяла картинку из интернета =)

Это нижняя граница, установленная по ТЗ. И сделанная на клиенте. Аналогично можно оформить и верхнюю границу ввел больше 10 символов? Вокруг поля появляется красная рамочка. Значит, это тоже граница.


Итого по границе на клиенте


Граница на клиенте это значение атрибута maxlength в поле ввода символов. Больше этого значения ввести нельзя, система просто не даст этого сделать.


Это ограничение легко снять через панель разработчика Как снять maxlength со всех полей формы.

Это может быть не единственной границей. Границу разработчик может также прописать в код. Чтобы ее найти, следим за системой. Если она меняет своё поведение мы нашли границу:

  • Вводишь символы и у поля появляется красная рамка и подпись слишком большая длина граница! Помимо maxlength разработчик добавил проверку
  • Вводишь символы и появляются ошибки в консоли тоже граница!



2. Граница на сервере


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

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

Давайте снимем в Users ограничение maxlength = 10 с поля имя1, введем туда длинную строку и попробуем сохранить. При сохранении система выдает ошибку:



Вот это граница на сервере. Клиент передал серверу запрос, тот его отработал и вернул фидбек.

Осталось понять, где граница то. Конечно, в этом нам помогает сообщение об ошибке: actual: 37, maximum: 9. По идее, на сервере стоит ограничение в 9 символов. Но мы то с вами уже знаем, что это надо проверить!

Проверяем:

  • Вводим 9 символов, сохраняем сохранилось!
  • Вводим 10 символов сохранилось.
  • Вводим 11 символов при сохранении ошибка.

Значит, реально граница 10 символов на сервере. Совпадает с границей на клиенте, это хорошо.

А теперь давайте проверим поле хомячок.



Ага, тут граница другая. В сообщении 19, но мы помним, что оно врет. Проверяем граница 20 символов. А на клиенте то 10 было, в maxlength! Значит, границы отличаются.

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

  • Сервер 10 символов
  • Клиент 20 символов

В итоге на клиенте ввести 20 символов можно, а при сохранении огребаем ошибку. Нехорошо!

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

Если система выдала ровно такую же ошибку, как и раньше, то ок. Значит, технологической границы нет. Ну и славненько. Главное, что мы попытались поискать =)

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

См также:
Технологическая граница в подсказках по ЮЛ если ввести 1000 символов, ничего не случится, а вот если войну и мир


3. Граница в БД


Граница в БД это сколько символов влезет в базу. Создавая базу, мы указываем размерность каждого поля. Вот пример из folks:

  • surname VARCHAR(255)
  • name VARCHAR(100)
  • city VARCHAR(20)

Это значит, что в поле фамилия мы можем сохранить 255 символов, в имя 100, а в город 20. При попытке запихать туда строку большего размера система выдаст ошибку из серии ORA-06502: PL/SQL: numeric or value error: character string buffer too small.

Важно понимать, что мы увидим границу БД только в том случае, если нет границы на сервере. Путь поиска границы не меняется:

  1. Сняли границу на клиенте
  2. Запихали большую строку
  3. Пытаемся сохранить



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



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



Конечно, может быть и другая ситуация когда на сервере граница больше, чем в БД:

  • Сервер 20 символов
  • БД 10 символов

И тогда получается забавная ситуация. Да, от вставки войны и мир мы защитились. Вводишь 25 символов или 25 млн символов получаешь осмысленную ошибку. А вот если вводишь 11 символов, то ой! Большой и страшный стектрейс во весь экран.

Поэтому при тестировании черного ящика не стремитесь сразу фигачить МНОГО символов. Для начала попробуйте осмысленное значение. А если вы сняли ограничение на клиенте, стоит попробовать пограничное значение к нему. Было maxlength = 10? Попробуйте 11 символов. А потом уже 55, а потом уже 55 млн.


Итого: чек-лист поиска границ


В клиент-серверном приложении границы могут быть в каждом звене. И в идеале они должны совпадать. Но мы должны это проверить!



Границ может быть больше. На клиенте разработчик может навесить сразу несколько границ: и maxlength, и изменение поведения при пересечении некой черты (js-код).

Границ может быть меньше. Из серии да не будет пользователь глупости вводить, и в итоге на клиенте и сервере вообще без ограничений. Но размерность полей в БД остается.

Как искать границы:

1. Проверить, есть ли на поле maxlength это самая очевидная граница на клиенте.

2. Снять это ограничение.

3. Включить консоль JS (а куда же без нее?).

4. Начать вбивать символы, около 50-100 в каждое поле. Следить за:

  • консолью не появились ли ошибки?
  • поведением самой системы не изменилось ли поведение?

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

5. Попробовать сохранить эти 50-100 символов. Так мы ищем границу на сервере и / или в базе.

Если система выдаст осмысленную ошибку вида Слишком длинное поле это ошибка на сервере. Если ошибка необработанная скорее всего, на сервере границы нет, и вы нашли границу в базе.

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

6. Ввести 100 млн символов (инструменты) и попробовать их сохранить для поиска технологической границы.

В процессе статьи мы проверили по этому чек-листу в системе Users поля имя1 и хомячок. Результаты:

Поле имя1:

  • maxlength 10 символов
  • сервер 10 символов

Поле хомячок:

  • maxlength 10 символов
  • js 11 символов
  • сервер 20 символов

Для имени все хорошо, границы совпадают. А вот при исследовании хомячка мы обнаружили сразу 2 проблемы разница границ клиент-сервер (10 и 20), а также ошибка в консоли. Можно ставить баги! =)
Подробнее..

Из песочницы Подготовка к собеседованию QA starter pack или самая большая шпаргалка вопросов-ответов по тестированию

18.07.2020 14:05:59 | Автор: admin

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

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

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

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

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

Эта статья не заменит хорошую книгу или курс, с другой стороны, здесь больше реальности.

ВНИМАНИЕ! Для того, чтобы увидеть материал целиком, нужно открыть первую или вторую часть в файлах на гитхабе (Manual part 1 или Manual part 2).

Оказалось, что такой объем практически нереально предоставить общественности. К сожалению, Хабр пока не умеет парсить исходники больше 150кб, а это означало бы разбиение материала на 8 статей (разработчики пообещали исправить, задача уже в активных).

На гите дополнительно пришлось поделить материал на 2 файла ввиду ограничения на размер одного в 1 мб. Если есть что исправить или дополнить пишите тут/создавайте issue/форкайте и коммитьте! Призываю участвовать всех неравнодушных!

Оглавление


Manual part 1


  • HR-часть
  • Вопросы с реальных собеседований с этапа HR
  • Общее о тестировании
  • Что означает тестирование ПО?
  • Почему требуется тестирование ПО?
  • Что означает обеспечение качества (Quality Assurance QA) при тестировании ПО?
  • Что означает контроль качества (Quality Control QC) при тестировании ПО?
  • Что означает качество ПО? (Software Quality)
  • Объясните отличия в QA, QC и тестировании
  • Что означает Verification при тестировании ПО?
  • Что означает Validation в тестировании ПО?
  • Разница между Design Verification и Design Validation?
  • Принципы тестирования?
  • Что подразумевается под тестовым покрытием? (Test Coverage)
  • Что такое модель зрелости тестирования (TMM Test Maturity Model)?
  • Что такое CMM?
  • Что такое тестирование со сдвигом влево? (Shift left testing)
  • Что такое независимое тестирование? (Independent testing)
  • В чем разница между превентивным и реактивным подходами в тестировании? (Preventative and Reactive approaches)
  • Перечислите типичные возможные обязанности инженера по обеспечению качества?
  • Что такое аудит качества?
  • Почему тестирование делится на отдельные этапы?
  • Почему невозможно полностью протестировать ПО?
  • Как вы тестируете продукт, если требования еще не зафиксированы?
  • Как вы узнаете, было ли создано достаточно тестов для тестирования продукта?
  • Как вы понимаете инспекцию?
  • Какие есть роли/должности в команде?
  • Опишите жизненный цикл продукта по этапам какие участники на каждом этапе, какие у них роли? Какие артефакты на каждом этапе?
  • Кто такой SDET?
  • Что такое тестирование как сервис? (TaaS testing as a Service)
  • Что подразумевается под тестовой средой? (Test Environment/Test Bed)
  • Что подразумевается под тестовыми данными?
  • Основные фазы тестирования?
  • Подробнее про бета-тестирование?
  • Что означает пилотное тестирование? (Pilot)
  • В чем отличие build от release?
  • Что такое бизнес логика (domain)?
  • Ты единственный тестировщик на проекте. Что делать?
  • Основные инструменты тестировщика?
  • Виды тестирования
  • Какие существуют основные виды тестирования ПО?
  • Типы тестирования? (White/Black/Grey Box)
  • Что означает тестирование черного ящика?
  • Что означает тестирование белого ящика?
  • Что означает тестирование серого ящика? (Grey box)
  • Основные отличия White/grey/black box?
  • Что такое деструктивное/разрушающее/негативное тестирование? (DT Destructive testing)
  • Что такое недеструктивное/неразрушающее/позитивное тестирование? (NDT Non Destructive testing)
  • Что такое пирамида/уровни тестирования? (Testing Levels)
  • Что подразумевается под компонентным/модульным/юнит тестированием? (Component/Module/Unit testing)
  • Что подразумевается под интеграционным тестированием? (Integration testing)
  • Разница между Unit testing и Integration testing?
  • Что такое системное интеграционное тестирование? (SIT System Integration testing)
  • Что подразумевается под инкрементальным подходом? (Incremental Approach)
  • Что подразумевается под подходом снизу-вверх? (Bottom-Up Approach)
  • Что подразумевается под подходом сверху-вниз? (Top-Down Approach)
  • Что подразумевается под гибридным/сэндвич-подходом? (Sandwich Approach)
  • Что подразумевается под подходом Большого взрыва? (Big Bang Approach)
  • В чем разница между тест-драйвером и тест-заглушкой? (Test Driver and Test Stub)
  • Что подразумевается под системным тестированием?
  • Можем ли мы провести системное тестирование на любом этапе?
  • Что такое функциональное тестирование?
  • Что такое тестирование совместимости/взаимодействия? (Compatibility/Interoperability testing)
  • Что такое тестирование на соответствие? (Conformance/Compilance testing)
  • Что такое нефункциональное тестирование?
  • Основные понятия в тестировании производительности?
  • Тестирование производительности клиентской части и серверной, в чем разница?
  • В общем виде что такое тестирование производительности?
  • Что такое тестирование емкости/способностей? (Capacity)
  • Что означает тестирование масштабируемости? (Scalability)
  • Разница между тестированием ёмкости/способностей и тестированием масштабируемости? (Capacity vs Scalability)
  • Расскажите о стрессовом тестировании? (Stress testing)
  • Расскажите о нагрузочном тестировании? (Load)
  • Что такое объемное тестирование? (Volume testing)
  • Тестирование выносливости/стабильности/надежности (Soak/Endurance/Stability/Reliability testing)
  • Что такое спайк/шиповое тестирование? (Spike)
  • Что такое тестирование устойчивости? (Resilence)
  • Что такое тестирование времени отклика? (Response time testing)
  • Что такое Ramp тестирование?
  • Что такое тестирование хранилища? (Storage testing)
  • Что такое тестирование на отказ и восстановление? (Failover and Recovery testing)
  • Что вы знаете о Тестировании удобства пользования? (Usability testing)
  • Отличия тестирование на удобство пользования и тестирования доступности? (Usability Vs. Accessibility testing)
  • Что такое тестирование интерфейса? (UI testing)
  • Что такое тестирование рабочего процесса/воркфлоу? (Workflow testing)
  • Что вы знаете о пользовательском приемочном тестировании? (UAT User Acceptance testing)
  • Что такое эксплуатационное приемочное тестирование? (OAT Operational Acceptance testing)
  • Расскажите об инсталляционном тестировании?
  • Что вы знаете о тестировании безопасности? (Security and Access Control testing)
  • Что означает оценка уязвимости/защищенности? (Vulnerability Assessment)
  • Расскажите подробнее о тестировании на проникновение? (Penetration testing)
  • Отличия Vulnerability Assessment от Penetration testing?
  • Что такое Fuzz тестирование?
  • Можно ли отнести тестирование безопасности или нагрузочное тестирование к функциональным видам тестирования?
  • Что вы знаете о конфигурационном тестировании? (Configuration testing)
  • Что подразумевается под проверкой дыма/дымовым тестированием? (Smoke testing)
  • Что такое тестирование встряхиванием? (Shake out testing)
  • Что подразумевается под санитарным тестированием/согласованности/исправности? (Sanity testing)
  • Отличие санитарного тестирования от дымового? (Sanity vs Smoke testing)
  • Что вы знаете про регрессионное тестирование? (Regression testing)
  • Объясните, что такое тестирование N+1?
  • Что означает подтверждающее тестирование? (confirmation/re-testing)
  • В чем разница между повторным и регрессионным тестированием?
  • Типы регрессии по Канеру?
  • Что вы знаете о тестировании сборки? (Build Verification Test)
  • Что такое тестирование файлов cookie?
  • Что такое тестирование потоков? (Thread testing)
  • Что такое тестирование документации? (Documentation testing)
  • Какие вы знаете уровни тестирования данных?
  • Что такое подкожный тест? (Subcutaneous test)
  • Расскажите о локализации, глобализации и интернационализации? (Localization/ globalization/internationalization testing)
  • Что такое исследовательское тестирование? (Exploratory testing)
  • Что такое Свободное или Интуитивное тестирование? (Adhoc)
  • Что вы знаете о мутационном тестировании? (Mutation testing)
  • Что означает механизм тестирования по ключевым словам? (Keyword Driven testing Framework)
  • Что вы знаете о тестировании интерфейса прикладного программирования (API Application Programming Interface)?
  • Как протестировать API без документации/черным ящиком?
  • А что такое endpoint?
  • Frontend testing Vs. Backend testing?
  • Что подразумевают под эталонным тестированием? (Baseline testing)
  • В чем разница между Baseline и Benchmark testing?
  • Что такое параллельное/многопользовательское тестирование? (Concurrency/Multi-user testing)
  • Как вы думаете, что такое тестирование на переносимость?
  • Что такое тестирование графического интерфейса/визуальное тестирование? (GUI Graphical User Interface)
  • Что такое A/B тестирование?
  • Что означает сквозное тестирование? (E2E EndtoEnd)
  • В чем разница между E2E и системным тестированием?
  • Что такое параллельное тестирование? (Parallel testing)
  • Тест дизайн
  • Тест дизайн? (Test Design)
  • Перечислите известные техники тест-дизайна?
  • Что такое статическое тестирование, когда оно начинается и что оно охватывает?
  • Что такое динамическое тестирование, когда оно начинается и что оно охватывает?
  • Какие виды Review вы знаете?
  • Что вы знаете о Data Flow testing?
  • Что вы знаете о Control Flow testing?
  • Что такое Loop coverage?
  • Что такое Race coverage?
  • Тестирование пути и тестирование базового пути? (Path testing & Basis Path testing)
  • Что вы знаете о Statement coverage?
  • Что вы знаете о Decision coverage?
  • Что вы знаете о Branch coverage?
  • Что вы знаете о Condition coverage?
  • Что вы знаете о FSM coverage?
  • Что такое Function coverage?
  • Что такое Call coverage?
  • Что означает LCSAJ coverage?
  • Сравнение некоторых метрик
  • Что такое Equivalence Partitioning?
  • Что такое Boundary Value Analysis?
  • Что такое Error Guessing?
  • Что такое Cause/Effect?
  • Что такое Exhaustive testing?
  • Какие вы знаете комбинаторные техники тест-дизайна?
  • Что такое тестирование ортогональных массивов? (OAT Orthogonal Array testing)
  • Что такое Domain analysis/testing?
  • Что такое Cyclomatic Complexity в тестировании ПО?
  • Что такое State Transition testing?
  • Что такое Scenario (use case) testing?
  • Что такое Decision Table testing?
  • Что такое Random testing?
  • Что такое Syntax testing?
  • Что вы знаете о Classification tree method?
  • Как мы узнаем, что код соответствует спецификациям?
  • Что включает в себя матрица отслеживания требований? (RTM Requirement Traceability Matrix)
  • В чем разница между Test matrix и Traceability matrix?
  • Что такое анализ GAP?
  • Что такое граф причинно-следственных связей? (Cause Effect Graph)
  • В чем разница между предугадыванием ошибок и посевом? (Error guessing and error seeding)
  • Стили тестов?
  • Техники тестирования требований?
  • Что такое эвристики?
  • Тестовые артефакты и документация (Test Deliverables/TestWare/test artifacts)
  • Виды тестовой документации?
  • Отличия Test Suite от Test Scenario?
  • Какие отличия у плана тестирования и стратегии тестирования?
  • Виды тест планов?
  • Что является основой для подготовки плана приемки? (PAP Product Acceptance Plan)
  • В чем разница между тест-кейсом и чек-листом?
  • В чем разница между тест-кейсами высокого уровня и низкого уровня?
  • Чем Test case отличается от сценария тестирования?
  • Что такое тест-анализ/основа теста? (Test Analysis/Test Basis)
  • Что такое документ бизнес-требований (BRD)?
  • Что вы знаете о требованиях (уровни/виды и т. д.)?
  • Рассажите, какие есть требования к самим требованиям?

Manual part 2


  • Дефекты и ошибки
  • Что такое дефект?
  • Классы дефектов?
  • Какие есть категории дефектов?
  • Error/Mistake/Defect/Bug/Failure/Fault?
  • Каково содержание эффективного сообщения об ошибке?
  • Несколько ключевых моментов, которые следует учитывать при написании отчета об ошибке?
  • Серьезность и Приоритет Дефекта (Severity & Priority)
  • Может ли быть высокий severity и низкий priority? А наоборот?
  • Жизненный цикл дефекта?
  • Пришёл баг из продакшена, что делаем?
  • Что такое утечка дефектов и релиз бага? (Bug Leackage & Bug Release)
  • Что означает плотность дефектов при тестировании ПО?
  • Что означает процент обнаружения дефектов при тестировании ПО?
  • Что означает эффективность устранения дефектов при тестировании ПО? (DRP)
  • Что означает эффективность Test case в тестировании ПО? (TCE)
  • Возраст дефекта в тестировании ПО?
  • Что такое принцип Парето в тестировании ПО?
  • Каковы различные способы применения принципа Парето в тестировании ПО?
  • В чем основное отличие отладки от тестирования? (Debugging Vs. Testing)
  • Почему в программном обеспечении есть ошибки?
  • Что вы будете делать, если во время тестирования появится ошибка?
  • Как вы справляетесь с невоспроизводимой ошибкой?
  • Если продукт находится в производстве и один из его модулей обновляется, то необходимо ли провести повторную проверку?
  • Что такое анализ рисков?
  • В чем разница между coupling и cohesion?
  • Что такое скрытый дефект? (Latent defect)
  • Что такое маскировка ошибок, объясните примером?
  • Категории отладки? (Debugging)
  • Что такое Эффективность удаления дефектов? (DRE Defect Removal Efficiency)
  • Что такое сортировка дефектов? (Bug triage)
  • SDLC и STLC
  • Что вы знаете о жизненном цикле разработки ПО? (SDLC Software Development Lifecycle)
  • Что такое цикл/колесо Деминга? (Deming circle/cycle/wheel)
  • Модели разработки ПО?
  • Что такое Agile?
  • Что такое Scrum?
  • В чем отличие Canban от scrum?
  • Что знаете о User stories в гибких подходах к разработке?
  • Что значит жизненный цикл тестирования ПО? (STLC Software Testing Lifecycle)
  • Что вы знаете о техниках оценки теста? (Test Estimation)
  • В чем разница между SDLC и STLC?
  • Что такое быстрая разработка приложений? (RAD Rapid Application Development)
  • Что такое разработка через тестирование (TDD Test Driven Development)?
  • TDD в Agile Model Driven Development (AMDD)
  • Тестирование на основе моделей (MDD Model-driven Development)
  • Тестирование на основе данных (DDT Data Driven testing)
  • Тестирование на основе риска (RBT Risk Based Testing)
  • Что вы знаете о потоковом тестировании? (BFT BusinessFlowTesting)
  • Тестирование в разных сферах/областях (testing different domains)
  • Что такое веб-тестирование и как его производить?
  • Тестирование банковского ПО
  • Тестирование электронной коммерции (eCommerce)
  • Тестирование платежного шлюза (Payment Gateway)
  • Тестирование систем розничной торговли (POS Point Of Sale)
  • Тестирование в сфере страхования (Insurance)
  • Тестирование в сфере телекоммуникаций (Telecom)
  • Тестирование протокола: L2 и L3 OSI
  • Тестирование интернета вещей (IoT Internet of Things)
  • Что такое облачное тестирование? (Cloud testing)
  • Что такое тестирование сервис-ориентированной архитектуры? (SOA Service Oriented Architecture)
  • Что такое тестирование планирования ресурсов предприятия? (ERP Enterprise Resource Planning)
  • Тестирование качества видеосвязи WebRTC-based сервиса видеоконференций
  • Что такое тестирование ETL?
  • Мобильное тестирование
  • Каковы особенности в тестировании мобильных приложений? В чем отличия тестирования мобильного приложения от десктопного?
  • В чем отличия тестирования мобильного приложения от web?
  • Что вы знаете о симуляторах и эмуляторах?
  • Типы мобильных приложений?
  • Что основное проверить при тестировании мобильного приложения?
  • Последнее обновление Android/iOS, что нового?
  • Основные различия iOS и Android?
  • Виды жестов и т.п.?
  • Как проверить использование процессора на мобильных устройствах?
  • Объясните критические ошибки, с которыми вы сталкиваетесь при тестировании на мобильных устройствах или в приложениях?
  • Сети и около них
  • Что такое http?
  • Компоненты HTTP?
  • Методы HTTP-запроса?
  • Что такое ресурс?
  • Что такое веб-сервис? (WS Web service)
  • Отличие сервиса от сервера?
  • Отличие сервиса от веб-сайта?
  • Что такое REST, SOAP? В чем отличия?
  • Что такое JSON, XML?
  • Коды ответов/состояния сервера с примерами? (HTTP status code)
  • Почему ошибка 404 относится к 4** клиентской, если по идее должна быть 5**?
  • Какие еще бывают протоколы?
  • TCP/IP это?
  • Что такое куки (cookies)?
  • Разница между cookie и сессией/сеансом?
  • Отличие stateless и stateful?
  • Различия методов GET и POST?
  • Клиент серверная архитектура?
  • Уровни OSI?
  • Что вы подразумеваете под потоковыми медиа? (Streaming media)
  • Основные команды Linux?
  • Почему важно тестировать в разных браузерах?
  • Адаптивный веб-дизайн vs. Отзывчивый веб-дизайн, в чем разница? (Adaptive Vs. Responsive)
  • Как сервер узнаёт, с какого типа устройства/браузера/ОС/языка вы открываете веб-сайт? (Например, для Adaptive design)
  • Чем отличается авторизация от аутентификации?
  • Как работает авторизация/аутентификация? Как сайт понимает, что ты залогинен?
  • Почему важно делать подтверждение e-mail при регистрации?
  • Что такое кэш и зачем его очищать при тестировании?
  • Что такое AJAX в вебе?
  • Как работает браузер (коротко)?
  • Как работает сотовая связь?
  • Как работает подключение к Wi-Fi?
  • Базы данных
  • Может ли у ПО быть сразу несколько баз данных?
  • Что такое SQL?
  • Что вы знаете о NoSQL?
  • Что такое нормальные формы?
  • Понятие хранимой процедуры?
  • Понятие триггера?
  • Что такое индексы? (Indexes)
  • Какие шаги выполняет тестер при тестировании хранимых процедур?
  • Как бы вы узнали для тестирования базы данных, сработал триггер или нет?
  • Как тестировать загрузку данных при тестировании базы данных?
  • Основные команды SQL?
  • Подробнее о джойнах? (Join)
  • Типы данных в SQL?
  • ПРАКТИКА
  • Дана форма для регистрации. Протестируйте.
  • Определение серьезности и приоритета
  • Определение граничных значений и классов эквивалентности
  • Логические задачи
  • Еще примеры
  • Набор небольших задач по SQL
  • Тестирование чашки для кофе
  • HR: Как вы будете решать конфликты между членами вашей команды?
  • HR: Что делать, если разработчик утверждает, что найденный дефект таковым не является?
  • Вот тебе комп и работающий сайт. Сделай мне 401-ю ошибку.
  • С чего начать абсолютному новичку?
  • Путь
  • CV
  • Собеседование
  • Ошибки в работе у начинающих тестировщиков
  • Полезное
  • Youtube-каналы
  • Telegram
  • Web
  • Книги
  • Курсы
  • Источники
Подробнее..

Hiring Day в DINS получи оффер за один день

13.07.2020 20:06:08 | Автор: admin


DINS Hiring Day это возможность для талантливого QA Automation инженера получить оффер всего за один день. Весь процесс займет минимум усилий: нужно выполнить тестовое задание и принять участие в онлайн-собеседованиях 31 июля.


Подробности под катом.


Что мы делаем


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


Эта платформа помогает организовать удаленную работу и делает коммуникации простыми и удобными. Популярность этого решения растет вместе с нагрузкой на нашу команду. Поэтому мы в поисках QA Automation инженеров!


Кого ищем


Нам нужны QA Automation инженеры, которые:


  • Работают в области QAA два года или больше
  • Имеют опыт автоматизации тестовых сценариев на любом ООЯП
  • Владеют английским на уровне B1 и выше

С чем предстоит работать


Фронт работ широк: ты будешь разрабатывать авто-тесты и развивать тестовый фреймворк, тестировать системы, работающие на основе машинного обучения, заниматься анализом больших данных. Все это с использованием современные инструментов: MongoDB, Kafka, Postman, JUnit, TestNG, DynamoDB, Java 8, Java 11, Allure, Docker, Kubernetes и других.


Как принять участие


Подай заявку на нашем сайте и выполни тестовое задание до 26 июля.
Если все получится, тебе пригласят на онлайн-собеседования с командой и менеджером. Они состоятся 31 июля. В конце дня мы дадим обратную связь и, возможно, ты получишь свой оффер!


Как мы работаем


Сейчас удаленно, но планируем вернуться в офис. Оба наших офиса находятся в Санкт-Петербурге.


Не обязательно жить в Петербурге, чтобы участвовать в Hiring Day, но надо быть готовым к переезду. Мы с этим поможем: иногородним сотрудникам выдаем Relocation Bonus, помогаем с поисками жилья.



К офферу прилагаются и плюшки: ДМС со стоматологией, занятия английским, тренинги по soft skills, вечеринки и корпоративы.


Если ты готов к переменам, обладаешь нужными навыками хочешь работать над востребованным продуктом смело подавай заявку. В ответ ты получишь тестовое задание. Если остались вопросы пиши на hiringday@dins.ru.
Подробнее..

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

31.07.2020 00:18:01 | Автор: admin
В крупной компании джун с этим вопросом столкнется разве что на собеседовании. Можно рассказать общие принципы:
Скрытый текст
  • составление таблицы на 5-10 критериев отбора,
  • выбор, учитывая особенности приложения, характеристики реальных устройств и бюджет,
  • упомянуть, что девайс на руках не единственный вариант, частично можно протестировать эмуляторами и симуляторами и про фермы тоже не забыть.

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



Самый простой и быстрый вариант, взять готовый усредненный список предлагаемый BrowserStack


Хорошее распределение, но не забываем что во-первых это далеко не РФ, а во-вторых это бизнес проект и они предлагают те устройства которые есть у них. При разбросе в 2020 iPhone c 6 по 11, варианты 8 и XR очень близко, по Android слабовато с Huawei, не говоря уже про Xiaomi.

Если не ищете легких путей идем дальше.

  1. Первым делом запросите статистику у команды.

    Если вдруг она есть, да еще и подробная вы счастливчик и сюда заглянули скорее из любопытства. Если вам сказали что ее нет, не отступайте так сразу, может оказаться что таки есть, допустим, статистика сайта, но про это или не подумали, или решили что она не годится, или прошлая версия приложения, или был проект близкой тематики, но не взлетел. Любая статистика лучше ее полного отсутствия, даже если это не достоверные данные на конкретное приложение, а срез аудитории в вашей теме. Но рассматривайте эти варианты только как подсказку, а не как готовый список.
  2. Изучите целевую аудиторию (ЦА).

    Часто этим пунктом пренебрегают. Но он может быть важен. Приложение элитного Барбершопа нацелит вас на новые модели смартфонов, флагманы с большим экраном; в женском салоне предположительно увеличится процент айфонов и уменьшится любовь к формату Plus (модели iOS увеличенного размера с приставками Max, Plus). А если ваша ЦА средний класс в регионах тут будет большой разброс по производителям/устройствам, заметный процент старых моделей и Android в приоритете.
  3. Особенности самого приложения тоже могут влиять на выбор.

    Пообщайтесь с менеджером или разработчиками (как вариант редкий, но существующий изучите документацию), чтобы потом не оказалось, что в приложении графическом вы не можете протестировать поведение Pencil 2, потому что купили девайсы только с первым. Или ваше мобильное приложение сильно зависит от железа, а вы этот момент не учли и у всех ваших девайсов схожие характеристики. Узнайте и выпишите отдельно требования. Погуглите могут ли быть нюансы на разных устройствах при использовании ваших технологий (NFS, Fingerprint etc.) .
  4. Готовим шаблон.


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



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

    Производители. С них начинаем. На Android довольно высока девайсозависимость, производители стремятся привлечь покупателя фишечками только у нас, добавляя проблем разработчикам, поэтому важно протестировать приложения на устройствах разных вендоров.
    Напоминаю, что выше не готовый шаблон, вам будет необходимо актуализировать список во время выбора, исходя из статистики на тот момент времени (актуальной считается статистика не старше полугода), в идеале на вашу аудиторию.
    Для порядка записан Apple, но помните, что тестировать вы будете отдельно для каждой из платформ (iOS/Android), учитывайте это при дальнейшем выборе.
    Что тут делает Google и OnePlus будет во второй части.

    Посмотреть лидирующих (по трафику) вендоров можно на Statcounter



    Вверху мы видим актуальные данные за предшествующий месяц по выбранному региону, возможны варианты мир/Европа/Страна.
    На первом скриншоте РФ.
    Данные таблицы можно редактировать. Я выбрала длительный период, чтобы видна была динамика роста одних (Xiaomi c 4-х до 18%) и снижения процента присутствия у других (Lenovo c 7% до 1%, LG, Sony, Nokia). У Samsung незначительное снижение, с 28 до 24%.

    В РФ лидирует Samsung, у соседей впереди уже Xiaomi.





    В США заметное отличие: большой отрыв у Apple, заметная доля Samsung, далее LG, Motorola, Google, Huawei.



    Очевидно, что гео приложения важно.
    Мы (условно) выбираем девайсы для русскоязычного приложения на три страны: Россия (~2/3 аудитории), Украина, Беларусь.
    Samsung и Xiaomi включаем как обязательные.
    Huawei упорно не сдает позиции, обсудите с командой, поддерживаете ли (если не в теме, погуглите Huawei поддержка Google Play). Сюда же идет и Honor, по сути это тот же Huawei с маркетингом на молодежную аудиторию.
    Дополнительно зафиксируем Lenovo, LG, Sony, Nokia. У них примерно равные доли, скорее всего в первую выборку устройств они не попадут, но может сыграть какой-то из моментов описанных выше в пунктах 1-3.

    В качестве наглядной иллюстрации три диаграммы (Яндекс.Метрика) по мобильному трафику за полгода на трех русскоязычных сайтах с разной ЦА.
    Однозначно везде заметный охват у главной четверки: Apply, Samsung, Xiaomi, Huawei.
    А вот пятый игрок уже зависит от ЦА, у всех трех он различен: Sony, LG, Lenovo







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

    Соотношение сторон экрана. Важный параметр, про который иногда забывают, привязываясь по старой памяти в первую очередь к разрешению экрана, где сейчас уже зашкаливающее многообразие (Android).
    При этом проверять надо обязательно. На GUI тут живет много багов, и не редкость когда кнопка ставшая не там может в итоге привести к криту, заблокировав возможность использовать функционал приложения.
    Обратите внимание, в столбце первые три значения для планшетов, далее для смартфонов.
    При выборе параметров для смартфона постарайтесь захватить оба значения ближе к краю (из используемых) и среднее. В таблицу внесены соотношения сторон актуальные на середину 2020.
    На сегодня Sony выпускает новые смартфоны с вытянутыми экранами 21:9, пока это крайнее значение, но уже ходят разговоры про 23:9

    Размер. В этом столбце у нас три блока.
    Для начала мы тут отметили Планшет с вопросиком (обсудим позже).
    Далее идут отдельно варианты для Android и для iOS, т.к. у них немного отличается и подход и обозначение.

    Ценовой сегмент. Можете прописать конкретные цифры. Премиальным обычно считается сегмент выше $500, с учетом нынешних цен уже можно добавить и премиум + для устройств дороже $1000.
    Базовый сегмент $300-$500, бюджетный $150-300, то что дешевле из нижнего ценового сегмента.

    Новизна. За +++ принимаем современные устройства только что вышедшие на рынок, или ожидаемые к моменту утверждения бюджета и покупки, либо устройства вышедшие в текущем году. Для примера возьмем Apple. В эту категорию попадет премиум iPhone 12 во всех модификациях и базовый iPhone SE (2020)
    Под ++ идут устройства прошлого (2019) года iPhone 11 во всех 3-х модификациях.
    Один плюс для устройств трех предыдущих лет (2016-2018) это от семерки до 10-ки.
    К минусу отнесем то что младше. Да, 6-ки выпущенные шесть лет назад еще живы и вполне используемы. Вот статистика использования от DavidSmith

    Привязка по годам зависит от конкретного производителя и поддержки устройствами новых технологий. Не всякое устройство вышедшее на рынок в текущем году может получить +++.
    Разбивку по моделям/годам удобно смотреть на DeviceSpecifications .
    Но это мы уже немного забежали вперед. Про конкретные модели поговорим во второй части.

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

    Особенности. На iOS могут быть нюансы работы нативной Назад у моделей с монобровью/челкой, хотя физически она и расположена в зоне статус-бара. Любое приложение с ландшафтной ориентацией и полным использованием экрана (например плеер) так же желательно посмотреть на моделях с бровью. Если приложение использует камеру, обязательно проверять и на фронтальной, но этот пункт пойдет в столбец Дополнительно.
    Обратите внимание, чтобы в список попали устройства как с отсутствием на передней панели аппаратных кнопок так и с наличием. Для iOS это кнопка Home, для Android три сенсорных: Назад, Домой, Меню.
    Если у вас не веб, а приложение, рассчитанное не на премиум-сегмент, да еще и с записью данных на устройство работа с SD-картой иногда вызывает вопросы, включаем в список.

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

    Но перед переходом к выбору конкретных устройств, давайте снимем знак вопроса с одного важного пункта.

Планшеты


Обсуждаем с командой. Возможно у вас нативное приложение, которое вовсе не поддерживает этот тип устройств, тогда удаляем все лишнее в таблице и идем дальше.
Или ваше приложение активно поддерживает работу на планшете, и тогда однозначно планшеты нужны.
В среднестатистическом варианте придется решать.
Если бюджет ограничен, лучше взять несколько смартфонов, нежели планшет. Но если ваша аудитория планшетами пользуется хотя бы один планшет крайне желателен. Различий между двумя разными смартфонами и между смартфоном и планшетом, существенно больше и вероятность отловить баги выше. Хотя, скорее всего, это будет не функционал, а поехавшая верстка, на которую для небольшого стартапа без огромных амбиций пока можно призакрыть глаза (при этом помнить что имидж тоже важен). Выбор непростой.
Плохо, что если у вас своей статистики нет, по планшетам особо то и подсмотреть негде, особенно касательно РФ.
По миру вот свежие красивые цифры от Statista, процент имеющих планшет по странам. Если работаете на их рынок без планшета пожалуй не обойтись.


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

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

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

При этом, мы знаем, что iPad однозначный лидер среди планшетов.
По данным Statcounter у Apple 58% в мире, 50 в России, 40 в Украине и Беларуси.
По данным Statista на первое полугодие 2020 в мире 29,8% использования среди всех планшетов.


Т.е. цифру из статистики нам надо умножить то ли на 2 то ли на 3, чтобы оценить весь планшетный трафик.

Заглянем для начала в группу mp3 всего 0.1%. Вполне ожидаемо, тут с планшетом делать нечего.

Теперь посмотрим Дом и семья уже 1,1 %
Зайдем в раздел Литература ожидаемый рост, 1,6%
Проверим по живым сайтам, вот например открыта статистика у Lib.ru: Классика среднее 2,2% в выходные больше 3-х, и это не все планшеты, а только iPad


Анализируйте свою тему.
Только не забываем, что мы смотрим статистику сайтов, далеко не всегда ее можно спроецировать на приложение. Допустим вот тут мы видим всего 0,1 %. Перейдя на сайт, во-первых подмечаем кнопки установки приложений, во-вторых копирайт 2015 года. Приложение на Play Маркет более 5 млн установок, на App Store 38-ое в категории, поддерживает iPad и некогда было популярно. А по цифрам статы одного этого сайта можно было вынести вердикт планшет не нужен.

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

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

API для генерации ответов сервера с любыми кодами статусов

20.07.2020 14:14:41 | Автор: admin

Привет, Хабр! Работая над библиотекой-обёрткой REST API, я столкнулся с проблемой. Для тестирования обработки ошибочных кодов ответа сервера (400, 500, 403 и т.д.) необходимо искусственно создавать условия на сервере для получения соответствующих кодов. При правильно настроенном сервере, например, непросто получить ошибку 500. А тестировать функции-обработчики ошибок как-то надо. Я написал небольшое API, которое генерирует ошибочные ответы сервера httpme.tk


Как применять в тестировании?


Например, есть такой код (python3):


from requests import session as requests_sessionsession = requests_session()session.hooks = {    'response': lambda r, *args, **kwargs: raise AccessError('Доступ закрыт, т.к. сервер подключен к другой БД') if r.status_code == 403  else pass}class AccessError(Exception):    """ 'своя' ошибка """    passdef getter(url):    return session.get(url)

Если кратко в коде есть функция, которая возвращает ответ сервера на GET-запрос на заданный URL, если в результате выполнения запроса возникает ошибка 403 вызывается внутреннее исключение модуля AccessError.


Этот код надо протестировать и отладить. Cоздать вручную условия для ошибки 403, а уж тем более, например, 500 (сервер слишком хорошо работает) довольно непросто. Тестировщику не важно, при каких условиях сервер выдаст ошибку 403: он тестирует не само API (например), а функцию, которая к нему обращается. Поэтому для тестирования вызова исключения при коде статуса 403 он может сделать вот так (python3 + pytest):


import pytestfrom mymodule import def test_forbidden():    with pytest.raises(AccessError):        getter('http://httpme.tk/403')

Как пользоваться?


Очень просто. Отправьте на сервер GET-запрос в формате http://httpme.tk/<status_code>. Например так (cURL):


curl -G http://httpme.tk/500

Или так (python3):


from requests import getget('http://httpme.tk/408')  # <Response [408]>

А что внутри?


А внутри маленькое Flask-приложение, вызывающее функцию abort(status_code) на каждый запрос.


Ссылка на GitHub


На этом всё!


Интересно услышать оценку полезности данного сервиса сообществом.

Подробнее..

Категории

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

© 2006-2020, personeltest.ru