Как определить окружение приложения тестирование
В каждом тестовом задании может быть несколько вариантов ответа. После проведения теста студенты могут попробовать обосновать свои неверные ответы.
В основные обязанности тестировщика входят:
- Выявление ошибки
- Исправление ошибки
- Составление отчета об ошибке
- Объяснение причины ошибки
- Написание тестов
Одному тест-требованию может соответствовать:
- только один тестовый пример
- несколько тестовых примеров
- не более двух тестовых примеров
Два тестовых примера проверяют один и тот же класс эквивалентности:
- если от них получен один и тот же результат
- если от них получена одинаковая реакция системы
- если они построены по одному тест-требованию
- если от них ожидается получить одинаковую реакцию системы
Сколько классов эквивалентности в общем случае выделяют для функции с двумя целочисленными входными параметрами и одним целочисленном выходным значением?
Ручное тестирование целесообразно применять:
- если тестовый пример не может быть выполнен в автоматическом режиме
- если тестовый пример построен по одному тест-требованию
- если автоматизация выполнения тестового примера очень сложна
- если автоматическое выполнение тестового примера требует много времени
7.2. Проверка домашнего задания
Студенты приносят заполненные отчеты об ошибках для тех модулей, которые они тестировали. Преподаватель оценивает их тест-планы и смотрит, удалось ли студентам найти все допущенные в методах ошибки.
7.3. Тестовое окружение
7.3.1Теоретическое вступление
Основной объем тестирования практически любой сложной системы обычно выполняется в автоматическом режиме. Кроме того, тестируемая система обычно разбивается на отдельные модули, каждый из которых тестируется вначале отдельно от других, затем в комплексе.
Это означает, что для выполнения тестирования необходимо создать некоторую среду, которая обеспечит запуск и выполнение тестируемого модуля, передаст ему входные данные, соберет реальные выходные данные, полученные в результате работы системы на заданных входных данных (рис. 7.1). После этого среда должна сравнить реальные выходные данные с ожидаемыми и на основании данного сравнения сделать вывод о соответствии поведения модуля заданному.
Тестовое окружение также может использоваться для отчуждения отдельных модулей системы от всей системы. Разделение модулей системы на ранних этапах тестирования позволяет более точно локализовать проблемы, возникающие в их программном коде. Для поддержки работы модуля в отрыве от системы тестовое окружение должно моделировать поведение всех модулей, к функциям или данным которых обращается тестируемый модуль.
Поскольку тестовое окружение само является программой (причем, часто реализованной не на том языке программирования, на котором написана система), оно тоже должно быть протестировано. Целью тестирования тестового окружения является доказательство того, что тестовое окружение никаким образом не искажает выполнение тестируемого модуля и адекватно моделирует поведение системы.
Тестовое окружение для программного кода на структурных языках программирования состоит из двух компонентов – драйвера, который обеспечивает запуск и выполнение тестируемого модуля, и заглушек, которые моделируют функции, вызываемые из данного модуля. Разработка тестового драйвера представляет собой отдельную задачу тестирования, сам драйвер должен быть протестирован, дабы исключить неверное тестирование. Драйвер и заглушки могут иметь различные уровни сложности, требуемый уровень сложности выбирается в зависимости от сложности тестируемого модуля и уровня тестирования. Так, драйвер может выполнять следующие функции:
- Вызов тестируемого модуля
- 1 + передача в тестируемый модуль входных значений и прием результатов
- 2 + вывод выходных значений
- 3 + протоколирование процесса тестирования и ключевых точек программы
Заглушки могут выполнять следующие функции:
Тестовое окружение для объектно-ориентированного ПО выполняет те же самые функции, что и для структурных программ (на процедурных языках). Однако, оно имеет некоторые особенности, связанные с применением наследования и инкапсуляции.
Если при тестировании структурных программ минимальным тестируемым объектом является функция, то в объектно-ориентированным ПО минимальным объектом является класс. При применении принципа инкапсуляции все внутренние данные класса и некоторая часть его методов недоступна извне. В этом случае тестировщик лишен возможности обращаться в своих тестах к данным класса и произвольным образом вызывать методы; единственное, что ему доступно – вызывать методы внешнего интерфейса класса.
Существует несколько подходов к тестированию классов, каждый из них накладывает свои ограничения на структуру драйвера и заглушек.
- Драйвер создает один или больше объектов тестируемого класса, все обращения к объектам происходят только с использованием их внешнего интерфейса. Текст драйвера в этом случае представляет собой т.н. тестирующий класс, который содержит по одному методу для каждого тестового примера. Процесс тестирования заключается в последовательном вызове этих методов. Вместо заглушек в состав тестового окружения входит программный код реальной системы, соответственно, отсутствует изоляция тестируемого класса. Именно такой подход к тестированию принят сейчас в большинстве методологий и сред разработки. Его классическое название – unit testing (тестирование модулей).
- Аналогично предыдущему подходу, но для всех классов, которые использует тестируемый класс, создаются заглушки
- Программный код тестируемого класса модифицируется таким образом, чтобы открыть доступ ко всем его свойствам и методам. Строение тестового окружения в этом случае полностью аналогично окружению для тестирования структурных программ.
- Используются специальные средства доступа к закрытым данным и методам класса на уровне объектного или исполняемого кода – скрипты отладчика или accessors в Visual Studio.
Основное достоинство первых двух методов – при их использовании класс работает точно таким же образом, как в реальной системе. Однако в этом случае нельзя гарантировать того, что в процессе тестирования будет выполнен весь программный код класса и не останется непротестированных методов.
Основной недостаток 3-го метода – после изменения исходных текстов тестируемого модуля нельзя дать гарантии того, что класс будет вести себя таким же образом, как и исходный. В частности, это связано с тем, что изменение защиты данных класса влияет на наследование данных и методов другими классами.
Тестирование наследования – отдельная сложная задача в объектно-ориентированных системах. После того, как протестирован базовый класс, необходимо тестировать классы-потомки. Однако, для базового класса нельзя создавать заглушки, т.к. в этом случае можно пропустить возможные проблемы полиморфизма. Если класс-потомок использует методы базового класса для обработки собственных данных, необходимо убедиться в том, что эти методы работают.
Таким образом, иерархия классов может тестироваться сверху вниз, начиная от базового класса. Тестовое окружение при этом может меняться для каждой тестируемой конфигурации классов.
Сколько в вашем проекте тестовых стендов — 5, 10 или больше 10? Навскидку, нужны стенды для каждой команды разработки, стенды для QA под каждый проект, менеджерам проектов тоже нужны стенды, а еще CI — трудно это все точно разграничить и не вызвать конфликтные ситуации. Одним словом, почему бы нам не делать тестовый стенд ровно тогда, когда он нужен? Нужен сейчас тестовый стенд — мы его сделали, не нужен — мы его удалили.
Именно такой подход предложил Александр Дубровин (adbrvn) на Highload++ 2017 в своем докладе, расшифровку которого вы найдете под катом.
О спикере: Александр Дубровин работает в Superjob. Известно, что проекты этой компании высоконагруженные. Но сегодня мы не будем говорить о том, сколько пользователей посещают портал, и сколько данных хранится на серверах, а затронем другие показатели.
Забегая вперед, скажем, что, на самом деле, Superjob не знают, сколько у них тестовых стендов. Но обо всем по порядку. Начнем с небольшой истории.
Немного истории
Представим себе небольшой проект S. В нем есть команда разработчиков, которым надо где-то тестировать свой код. Чтобы организовать тестирование, мы поставим тестовую машину, сделаем ее похожей на продакшен, накатим туда код, запустим и разработчики смогут там что-то тестировать.
В какой-то момент команда начинает расти, и необходим штат тестировщиков. Появляются QA-специалисты, и им тоже нужно где-то тестировать.
Можно использовать простой подход — выделить какой-то участок для тестировщиков, накатить туда такую же копию, и вот они уже могут тестировать. Все замечательно и хорошо!
Проект продолжает расти, и появляется дополнительная команда разработки. Им также требуется где-то что-то тестировать. Подход уже знаком — мы отделяем еще одну часть тестового сервера.
Но на самом деле команды тоже растут — по одному тестовому стенду им становится мало. Задач они тоже делают больше, поэтому тестировщикам нужно много тестировать.
Примерно в такой стадии можно начать замечать интересные истории. Допустим, есть тестировщик Вася, который хочет протестировать какую-то задачу. Он выбирает тестовый стенд, раскатывает туда код и начинает тестировать. Кликает, кликает и понимает, что что-то не то, что-то не работает, и вообще задача не сделана.
В JIRA начинают падать тикеты, возле Васи начинают собираться разработчики со словами: «Да как же так? Все же сделано!» и кто-то наконец спрашивает: «А у тебя какая ветка на тест раскатана?» Вася смотрит — не та. Ветка быстро исправляется, тикеты в JIRA закрываются, все хорошо. Вася продолжает тестировать, у него все работает.
Но в это время в другом конце комнаты разработчик Вова думает: «Странно, а почему у меня не работает?» Но он быстро понимает, что ветка не та. Раскатывает ту, что нужно, и проблемы снова у Васи.
За пару итераций они понимают, что они просто тестируют на одном тестовом стенде и мешают друг другу. В результате время потрачено впустую, Вася и Вова недовольны.
Другая история. Разработчик Коля знает про Васины проблемы, заранее приходит к нему и спрашивает, какой тестовый стенд сейчас свободен. Вася указывает свободный, и все хорошо. Через пару дней они встречаются снова, и Вася спрашивает у Коли: «Ты нам тестовый стенд вернешь? Ты его занимал на часок, а уже 2 дня прошло».
И снова проблема — либо разработчику искать другой стенд, либо все будут бодро ждать, пока он закончит тестирование.
На самом деле на схеме выше отображено не все. Здесь не хватает менеджеров. Иногда менеджеры хотят смотреть еще не протестированный сырой код. Подход стандартный — мы снова выделяем уголок тестового сервера и делаем еще тестовые стенды.
Самая последняя часть этой системы — это конечно CI — он тоже хочет куда-то стрелять, где-то тестировать.
Плавно развивая такую схему, мы получаем бесконтрольное изменение тестовых стендов. Схема плоха тем, что мы действительно не контролируем такие стенды — мы не знаем:
- кто тестирует на этом стенде в данный момент;
- что там раскатано;
- мы вообще не знаем, занят он сейчас или свободен.
В этот момент мы задумались — что же делать? Зачем нам столько тестовых стендов? Почему бы нам не делать тестовый стенд ровно тогда, когда он нужен? Нужен сейчас тестовый стенд — мы его сделали, не нужен — мы его удалили.
Следующий шаг в этой идее — делать тестовый стенд под каждую ветку кода.
Вроде идея хорошая, но есть технические нюансы. Нам нужны стенды:
- независимые;
- очень похожие на продакшен — мы хотим тестировать ближе к продакшен-окружению;
- с возможностью быстро создавать стенды, потому что мы хотим, чтобы, как только ветка появилась, тут же под нее появился бы и тестовый стенд.
Суровая реальность
Еще есть суровая реальность, в которой у нас:
Сказано — сделано!
Docker/docker-compose
Во-первых, мы говорили о том, что тестовые стенды должны быть изолированными и максимально похожими. В наше время это позволяет реализовать docker. Он даст возможность запускать контейнеры. Очевидно, что одним контейнером мы не обойдемся, более того, нам надо запускать кучу похожих стеков. Поэтому нужен docker-compose.
Замечательно — мы будем использовать docker — это стильно, модно, молодежно.
Распиливаем монолит выделяем сервисы
Docker пропогандирует микросервисный подход и здесь мы встаем перед проблемой, потому что у нас монолит.
Вы когда-нибудь пробовали оценить, сколько стоит распилить монолит по микросервисам? Очевидно, что эта цифра измеряется в человеко-годах.
В какой-то момент мы посмотрели на компонентную схему нашей системы и увидели, что здесь у нас есть load-balancing, здесь — приложение на php, здесь — node.js-приложение. Почему бы нам не запускать именно это, как сервис. Давайте найдем то, что мы можем запускать в docker-контейнерах.
Настраиваем сеть
Дальше нам необходимо каким-то образом достучаться до нашего тестового стенда. Естественно, нам необходимо вытащить 80-ый порт наружу, чтобы браузер мог открыть наш тестовый стенд, но если такие стенды будут запускаться в рамках одной машины, нам нужно выдавать IP.
В документации имеется целый огромный раздел про настройку сетей.
Docker умеет использовать различные типы сетей. В нашем случае очень помогла сеть типа macvlan. Это технология, которая позволяет на одном физическом сетевом интерфейсе реализовывать пачку виртуальных сетевых интерфейсов. При этом docker сам будет управлять этими интерфейсами: создавать, добавлять на машину и получать уже внешние, по отношению к хост-машине, IP-адреса.
Таким образом мы можем запустить пачку контейнеров, дать фронт-контейнеру (балансеру) возможность получить внешний IP-адрес и открыть на нем 80-ый порт. Мы уже можем постучаться туда при помощи браузера.
Поднимаем DNS и API
Мы помним, что у нас есть доменные зоны и куча поддоменов. Таким образом, обратиться к тестовому стенду мы можем только по домену 2-го уровня. Здесь есть как колоссальный плюс, так и колоссальный минус:
- Минус — нам очевидно придется каким-то образом перекрывать реальные домены в зоне ru, ua, uz, by.
- Плюс — в качестве домена 2-го уровня можно зашить прямо конкретное название ветки — мы же делаем тестовые стенды под каждую ветку кода.
Минус обходится на самом деле просто. Если нам приходится перекрывать домены, мы просто добавляем префикс и таким образом ограничиваем набор перекрываемых доменов — с этим уже можно мириться.
В нашем случае мы выбрали префикс sj. Получается, нам приходится перекрывать домены только с префиксом sj — таких явно немного.
Еще одна часть DNS — это API. Как уже говорилось, необходимо поднимать тестовые стенды быстро. Поэтому нам нужен DNS-сервер, который позволяет быстро добавлять и быстро убирать запись по API в автоматическом режиме.
Решение — PowerDNS. Этот сервер позволяет достаточно быстро и просто прикрутить к нему API и при помощи скриптов добавлять и удалять тестовые стенды.
Замечательно! Мы подняли и настроили DNS, научили наши контейнеры в него прописывать свои IP, но чего-то не хватает.
Делаем SSL-CA
Мы живем в XXI веке. Очевидно, что весь интернет — SSL и тестовые стенды должны поддерживать SSL. Достаточно много багов специфичны для SSL, и mixed content — только вершина айсберга.
Итого, нам нужен способ быстро получить сертификат и быстро его применить на поднимающийся тестовый стенд. В нашей компании уже был центр сертификации, основанный на OpenSSL. Здесь мы пошли простым методом написания своего велосипеда.
Велосипед пишется за один день и позволяет при помощи GET-запросов получать сертификаты, сгенерированные уже на конкретное имя домена.
Осталось самое малое. Нужно это автоматизировать, потому что мы же хотим все это делать одной кнопкой.
Автоматизируем
Для себя на начальном этапе мы написали консольный скрипт, который позволяет просто поднять тестовый стенд или его удалить.
Очевидно, тестировщикам это не очень удобно. Поэтому можно, например, сделать специальную сборку, которая соберет тестовый стенд и запустит его.
Но на самом деле самый крутой шаг в этом плане — это добавить такую кнопку прямо в JIRA-тикет. Представьте, ваш тестировщик открывает JIRA-тикет, читает требование, нажимает кнопку и получает через пару минут тестовый стенд — здорово же?
Плюсы
- Изначально мы планировали это именно под ручное тестирование, чтобы тестировщик мог запустить и прокликать любую версию своего кода, и это круто заработало.
- Следующий дополнительный бонус — у нас появились демо-хосты. Это то же самое, только в JIRA-тикет заходит не тестировщик, а менеджер проекта. Он тоже может посмотреть и покликать сырой код.
- Мы получили колоссальный плюс для CI. Когда мы обучили CI точно так же поднимать тестовый стенд на конкретную версию кода и потом его удалять, у нас появилась возможность запускать абсолютно любые тесты для любой ветки. Даже самые сложные интерфейсные selenium тесты я могу одним кликом прогнать для любой ветки в моем проекте.
Минусы
Но есть и минусы, я бы сказал, нюансы в управлении стендов. Нужно научиться правильно ими управлять.
Первую версию своей системы мы выкатили на старый слабый сервер и настроили создание тестовых стендов под каждую новую ветку. Конечно, где-то через день-полтора сервер не справился — просто потому, что веток появляется очень много.
Тогда, мы перестали их создавать автоматически, а появилась кнопка в JIRA, CI научилась запускать и останавливать тестовые стенды, собирать с них логи.
Определенно есть класс задач, которые эта система не позволит решать. Например, часто всплывающая проблема — это общее время для всех контейнеров. Некоторые задачи было бы удобно тестировать, сдвинув время на сервере. Эта система, к сожалению, не позволяет решать такие задачи. Но такие задачи можно решить, добавив в код специальные ветки для тестирования, чтобы можно было, например, посмотреть, как поведет себя форма через 2 недели.
На входе мы имели систему тестовых стендов, которая заставляла нас искать тестовый стенд и не гарантировала нам то, что никто не будет мешать друг другу на этих тестовых стендах.
Было: «Вася, а какой тестовый свободный — мне свою задачу раскатить потестировать».
На выходе получилась одна кнопка, которую можно нажать и получить через пару минут готовый тестовый стенд под конкретную версию кода. Даже если этим стендом будет пользоваться несколько человек, гарантировано то, что эти люди хотят смотреть именно эту версию кода.
Стало: «Жму кнопку и через полторы минуты получаю новый тестовый стенд под конкретную задачу».
Бонусом мы получили все тесты в один клик. Как я уже говорил, любые тесты на любой ветке прямо из CI выбираются одной кнопкой. Дальше машина все сделает сама: поднимет тестовый стенд, обстреляет его, соберет с него логи и удалит.
Возвращаясь к своему первому вопросу, сколько же тестовых стендов нам нужно? Я не знаю, сколько нам нужно тестовых стендов, потому что сегодня их нужно 20, завтра — 15, послезавтра 25.
Но я точно знаю, что у нас ровно столько тестовых стендов, сколько нужно здесь и сейчас.
Время летит незаметно, и до фестиваля конференций РИТ++ осталось совсем немного, напомним он пройдет 28 и 29 мая в Сколково. Пользуясь случаем, приводим небольшую подборку заявок RootConf для широкого круга слушателей:
Тестовое окружение - приятный уютный мирок. Сразу скажу, что я конечно ЗА, когда это возможно устроить в проекте, но не всегда это возможно по разным причинам, а в каких-то проектах разработчики вообще не видят продакшена. А так, сидишь себе - добавил таблицу, убрал, добавил, убрал и нет проблем! Поменял код, проверил, тестики напилил - чудеса же просто! Однако за все нужно платить, об этом и поговорим.
Плюсы тестового окружения очевидны - во-первых, продакшн не разлетается от ваших душевных метаний. Далее по списку:
- Вы можете «всё» (потом объясню почему в кавычках) протестить до выкатки в прод.
- Вы можете написать тесты и даже больше - не только вы можете их написать и при этом прод все еще будет работать =)
- Спокойно проверяете гипотезы
- Этот подход в целом стабильнее фиксов сразу на проде как в старые добрые
- Душевных метаний может быть слишком много и фича доезжает клиенту сильно сильно позже, чем могла бы.
- Если вы делаете рассылки или отчеты на основе данных, то - то кому и что конкретно уйдет в действительности надо проверить в итоге только на продакшн данных! Ох, как много раз я слышал, что «проверили на тестовых урезанных данных и оно все работало», но вот только у клиентов их данные не тестовые и хорошо бы на реальных тоже проверять отчет на быстродействие, а цифры перепроверить встречной проверкой
- Работа в тестовом окружении создает ложное впечатление безопасности и проверенности, но все равно не дает 100% защиты от косяков
Значит рассказываю кейс. Было дело, когда из-за того, что рассылка была проверена только на тестовых данных не заметили группу юзеров, которым по условиям тоже должна была быть отправлена почта. Ну и ушло им то, что не должно было уходить. Момент спорный согласен, скорее надо все письма сперва себе отправлять по возможности. Но при запросе в проде сразу же было видно, что что-то не так. Кто-то скажет, что руки кривые, а кто-то что данных в тестах не хватало, а итог один - попадалово. 😕
Патчи на БД - тоже отлично, на тестах идут быстро (так как данных в разы меньше и пр.), а в проде падают. Падают потому, что проверили плохо или вообще не все нюансы данных учли. Бывает просто фиговый запрос, который делает UPDATE всех строк, но вот только на проде таблица в 100 раз больше + под нагрузкой и патчи такие и сами не работают и другим не дают. У нас например много шард на террабайты данных и это в принципе быстро не идет, а есть еще и закрой кривой - то вообще беда.
Короче говоря, тестовая среда - отлично, но это не отменяет дополнительной перепроверки этого перед выкладкой в прод и после нее, да и в целом включения головы. Тестовая среда - тоже просто инструмент, который тоже дает сбои, а работает для клиентов продакшн и все инструменты должны использоваться для того, чтобы именно он в итоге был стабилен. Клиенту не важно на сколько хорошо у вас локально все работало.
Скажем так, суть поста в том, что тест - это не прод. И сразу после деплоя обязательно нужно смотреть метрики и логи - вдруг чё. Работа хорошего программиста не заканчивается за пределами IDE.
В этой статье рассмотрены вопросы, относящиеся к тестовому окружению. Среда тестирования (разработки, превью и д.р.), это набор программных и аппаратных средств, с которыми мы работаем. В случае тестирования ПО, это как минимум сервер и\или рабочая станция с операционной системой, ну а в принципе это могут быть и совокупность программ, и сетевая инфраструктура (устройства и ПО), и инструменты работы с ПО, и наборы данных. Далее в статье рассмотрим какие бывают среды тестирования, как, кем и дл чего они используются.
Вопрос технической реализации среды, это скорее вопрос конкретного технического решения, реализации в том или ином проекте, команде или организации. У вас может быть собрана, например, сложная сетевая топология из роутеров, рабочих станций и серверов для эмуляции реальной сети, установлено специальное ПО для управления и мониторинга трафика, а также реализованы средства для установки обновлений ПО. В этом плане важным будет ваш опыт, опишите то решение, с которым довелось работать.
С точки зрения общих вопросов, касающихся работы со средой тестирования нужно понимать следующие моменты:
1. Обычно существует разделение сред на среду разработки, тестирования и продуктивную (рабочая, «боевая»). Но это минимальный набор сред. Кроме них, также могут выделять, например, превью, интеграционную среду. Кратко для чего нужна каждая среда:
- Среда разработки (Development Env) – в ней разработчики пишут код, проводят отладку, исправляют ошибки, выполняют Unit-тестирование. За эту среду отвечают также разработчики.
- Среда тестирования (Test Env) – в этой среде работают тестировщики. Тут устанавливаются поставки ПО, которые выдают разработчики. Здесь тестировщики проверяют функционал, проводят регрессионные проверки, воспроизводят ошибки. За установки и настройки в тестовой среде может отвечать либо команда тестирования, либо отдельная группа поддержки (L3)
- Интеграционная среда (Integration Env) – иногда реализована в рамках среды тестированя, а иногда в рамках превью среды. В этой среде собрана необходимая для end-to-end тестирования схема взаимодействующих друг с другом модулей, систем, продуктов. Собственно, необходима она для интеграционного тестирования. Поддержка среды – также как и в случае со средой тестирования
- Превью среда (Preview, Preprod Env) – в идеале, это среда идентичная или максимально приближенная к продуктивной: те же данные, то же аппаратно-программное окружение, та же производительность. Она используется, чтобы сделать финальную проверку ПО в условиях максимально приближенным к «боевым». Здесь тестировщики проводят заключительное end-to-end тестирование функционала, бизнес и\или пользователи проводят UAT, а команды поддержки L3 и L2 выполняют DryRun (пробную установку релиза). Как правило за эту среду отвечает группа L3 поддержки.
- Продуктивная среда (Prodaction Env) – среда, в которой работают пользователи. С этой средой работает команда L2 поддержки устанавливая поставки ПО или патчи с исправлениями, выполняя настройки, отвечая за работоспособность всех систем. Инциденты и проблемы требующие исправления ПО передаются в работу команде на L3
2. Команда QA непосредственно работает со средой тестирования и интеграционной средой (часто она же превью среда).
3. Думаю, что схема Среды разработки и тестирования ПО сможет наглядно показать кто и на каких этапах работает с той или иной средой, а также кто эти среды поддерживает
Читайте также: