Роль архитектуры в автоматизации тестирования

Автор: Михаил Сидельников

Введение

В последнее время всё чаще при просмотре чьих-либо автоматических тестов я начал сталкиваться с рядом проблем, связанных во многом с  изначально слабой продуманностью их архитектуры.  Стало создаваться такое впечатление, что весь процесс написания автотестов сводится к 2 шагам - получение задачи от заказчика(во многом в его роли выступает начальник отдела тестирования или какой либо project manager) и написание кода. В этой цепочке слишком часто стал упускаться один важнейший шаг, а именно - проработка архитектуры тестов. В итоге всё это приводит к тому, что при желании модифицировать или добавить что-либо в тесты - приходится , как говорил один мой хороший знакомый, “переколбашивать” много уже написанного кода. А это - время, которое в современном мире очень дорого может стоить.


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

Начнем, наверное, с главного, а именно - что же это за зверь, автоматизация?

 

Что такое автоматизация тестирования?

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

Часто случается так - приходит начальник к инженеру по тестированию и говорит: “Привет, Вася. А давай-ка возьмем наши тесты и заавтоматизируем их! Это будет мега круто! Будем запускать их при каждом выходе билда и получать клевые результаты! А сами в это время будем сидеть и пить кофе!”. Со стороны не жизнь, а кайф. Но 99% кто так думает - ошибаются. Процесс автоматизации сложен, если к нему подходить правильно. А если подходить не правильно - то зачем он нужен в принципе?

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

 

Ошибки при автоматизации

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

Ошибка №1: Нам нужны автотесты.

Очень часто встречается одна из основных ошибок в процессе тестирования. Она заключается в том, что заказчик работы(под ним можно понимать какого-то , возможно, абстрактного начальника, project manager’а) приходит к инженеру по тестированию и говорит : “Нам нужны автотесты. Давай автоматизировать всё и вся, это должно круто упростить нашу жизнь и освободить кучу времени для других целей.” . Да пусть не обижаются на меня те, кого я тут назвал заказчиками, но такая ситуация часто встречается. Почему? Вопрос каждый может задать сам себе.

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

Тут наступает один из самых важных моментов в процессе автоматизации - необходимость принятия решения и его обоснования. Решения - а нужны ли автоматические тесты? Или можно написать очень подробный и хороший чеклист и прогонять тесты в ручную? Почему именно так? Ответ прост. Необходимо провести анализ ликвидности автотестов, то есть не будет ли потрачено слишком много времени на их написание и поддержку? Принимать это решение нужно на основе данных о том, насколько часто будет изменяться продукт и требования к этому продукту, или же подвергаемое тестированию решение будет разработано и , скажем, отдано кому-то, и изменений в нем не предвидится в ближайшее(и не очень) время. Так же хочется отметить, что понимание, что не всё нужно автоматизировать приходит лишь с опытом. Думаю 99% автоматизаторов, находящихся в начале своего пути в этом увлекательном направлении хотят автоматизировать всё и вся, и лишь спустя какое-то время понимают, что эта идея – плохая. Хочется отметить, что всё вышесказанное я взял не из воздуха, я сам прошел такой путь.

Приведу пример из моего опыта. Как-то, примерно год назад, подошел ко мне руководитель моей команды(да пусть не обижается на меня, если он это прочитает) и сказал: “Миша. Нам тут поступил заказ от соседнего отдела мониторинга на создание некоторого внутреннего(а в последствии возможно и внешнего) тула. Его задачей будет собирать данные из 5-7 таблиц в БД и отображение их на web страничке  в виде таблиц. Функционал меняться в 90% не будет. Давай напишем автотесты, чтобы тратить в будущем меньше времени на тестирование. Продукт уже готов на 70%, как только пройдем полный цикл тестирования – отдадим его парням из соседнего отдела, пусть пользуются.” Скажу по-честному – в моём мировоззрении в этот момент произошел небольшой переворот в понимании процесса автоматизации. До этого мне по сути ничего не нужно было другого, лишь бы мне дали что-нибудь заавтоматизировать. Сейчас объясню, какой и почему.

Я рьяно взялся за работу. Так как это было веб приложение – был выбран Selelium WebDriver + java, тем более что и в том и в другом у меня уже накопился достаточно солидный опыт. В течении пары дней я набросал некоторый каркас для тестов, разобрался, откуда и как берутся данные для таблиц в БД. Мне оставалась маленькая часть – достать данные из таблицы на web странице и сравнить их с полученными из базы. Вот тут-то и появилась проблема. Дело в том, что скроллинг в таблице с данными был устроен достаточно хитрым способом – данные подгружались постоянно по мере прокрутки. Поверьте, стандартными (и не очень) алгоритмами ,которые можно было применить, эти данные достать было очень и очень сложно. Скорее напрашивался некоторый костыль, который делать очень не хотелось(да, не люблю я это и вам не советую), да и сделать его было не просто. На изучение и продумывание , как же мне получить данные я потратил еще один день. Получается уже 3.

И тут я подумал : а зачем я это делаю? Зачем я пишу автотесты на тул, который мы сделали один раз, сдадим, он возможно , когда-то, через пол года немного поменяется, и то , по словам моего начальника, если такое случится – это будет скорее исключение. До меня в этот момент дошло, что автоматические тесты – они же не всегда нужны. Почему бы не создать просто хороший , понятный чеклист( о том, что же это такое, “понятный и хороший чеклист” я напишу чуть позже), в котором подробно опишу сценарии с примерами запросов и проверок. Прогонять его сможет каждый(например специалист по ручному тестированию), его очень мало надо будет поддерживать и для этого не надо никаких супер технических знаний в области написания кода. В отличии от автоматических тестов, в которых будет много вопросов: а что если поменяется версия браузера? что если я , например, уйду из компании? кто тогда будет их поддерживать и расширять если нужно(хочется отметить, что в команде тогда по сути не было людей, знакомых с selenium)? что будет, если поменяется каким то образом Selenium WebDriver, который я использовал при написании? При одном из этих раскладов, будет потрачена уйма времени на то, чтобы разобраться, что же происходит в этих тестах. Явный перевес в сторону чеклиста.

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

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

Подытожить этот раздел(как и следующие) хочется несколькими советами:

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

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

Ошибка №2: Чрезмерная автоматизация

Вторая ошибка. Предположим в команде уже есть набор из, скажем, 100 тестов. Ручных. Они описаны в каком то тест трекинг туле(или же просто в документе). Приходит указание, что с сегодняшнего дня 70% тестов должно быть автоматизировано. Поверьте, бывает и такое. Начинающий специалист по автоматизации тестирования подумает: ”Это же очень круто! Можно будет написать много тестов! Работа интересная и ее много” . Или другая ситуация: есть какой то сервер, наружу торчит API из примерно 40 методов. И кто-то принимает решение – покрыть все методы API автоматическими тестами. Принимается оно на основе мысли о том, что в данном случае автотесты будут сильно сокращать время на каждую итерацию тестирования сервера. Ситуации по сути своей - схожие. И приводят в итоге к одному и тому же результату. Какому - постараюсь описать. Как и , собственно, суть ошибки.

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

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

Потому что в грамотно продуманной архитектуре автотестов их поддержка и модификация должна быть элементарной и легковесной, именно по этому роль архитектуры очень важна, но об этом позже. Так же хочется отметить, что 2-3 тысячи тестов - это ужас еще и потому, что на их сборку уходит много времени, разобраться в них вновь пришедшему инженеру зачастую очень и очень сложно(а если не распологает очень хорошими навыками в написании кода - и того сложнее). Так было у меня в одном из прошлых проектов - количество тестов было примерно 3.5 тысячи. Сборка их длилась минут 5-7, причем иногда она падала из за ошибки OutOfMemory. “Сильно”, не правда ли?

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

  1. Совет 1: никогда не бойтесь сказать(разумеется приведя грамотные аргументы, базу для которых, например, можно почерпнуть из пары предыдущих абзацев), что “столько” тестов не нужно. Не бойтесь дойти с этими словами до большого руководства. Ведь в реальности, хороший специалист по автоматизации - это не тот, кто может много написать, а кто напишет столько, сколько нужно и может грамотно объяснить, почему именно такое количество - оптимальное и достаточное. Эту мысль я буду упоминать и в дальнейших разделах , потому что она очень и очень важна.
  2. Совет 2: всегда с осторожностью и особой вдумчивостью подходите к выбору подхода к автоматизации. Это сэкономит время ваше и ваших коллег как сейчас, так и в будущем. Никогда нельзя думать, что “Я сейчас напишу тесты на java, и пусть все, кто работают, тоже пишут так же”. Это очень плохой подход. Грамотнее задать себе вопросы: “Я могу написать тесты на java, а другие смогут? Те, кто будет их поддерживать. А если у них не будет технических скилов для написания кода(например тесты отдадут в отдел ручного тестирования)? Как они будут их исправлять в случае, если что то вдруг сломается? А что если мне сделать базу для тестов на java, она будет создана один раз и на века, а какой то интерфейс, которым могли бы пользоваться все, вынести, скажем, в xml файлы, через которые и будут писаться тесты? Тогда для расширения набора не потребуется знаний программирования! И тесты сможет писать любой тестировщик!”. Продумав ответы в начале этапа создания автотестов - вы покажете себя с лучшей стороны как инженер, который заранее подумал над будущей жизнью своих тестов и над тем, насколько удобно будет вашим коллегам. Ведь автоматические тесты(тут даже скорее нужно думать не над самими тестами, а над тем, что под ними, то, что сейчас чаще всего называют словом framework) - они созданы для облегчения трудов, а не для усложнения процесса. Возможно, более понятным данный совет станет после прочтения всего материала.

Ошибка №3: Использование ненужных библиотек

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

Данный абзац возможно будет где-то содержать более техническую терминологию и примеры, но постараюсь всё таки донести нужную мысль. Хочу привести пример: мне в руки попала одна библиотека, которую написал наш специалист по автоматизации. Суть ее была в генерации файла определенного формата с каким-то (не очень важно каким) наполнением. Моей задачей было синтегрировать эту библиотеку с тестами и прикрутить это всё к задаче, запускаемой на Jenkins. Заглянув в исходники, я увидел одну особенность - используется очень много сторонних библиотек. Подключаются они через maven, так что затрат времени на поиск jar файлов для подключения и сборки не требовалось.

Детально изучив код, я понял, что 2\3 библиотек используется не совсем понятно для чего! То есть ситуация следующая - что-то подключается к проекту, а функциональность этого “чего-то” используется максимум на 5%! А так как подключаются все библиотеки через maven , то ,следовательно, за ними тянется очень много других зависимостей! В итоге это приводит к тому, что сама библиотека весит достаточно много(по сути из за того, что в ней можно и не использовать) и , следовательно, время на ее сборку тратится лишнее(это уже второй минус, не очень фатальный, так как разница порядка 10 секунд). Причем основную массу этих подключаемых модулей можно заменить собственными , простейшими, занимающими порядка 5-10 строк , методами.

Генерация строк, дат, запись в файл - для всего этого были подключены сторонние модули, которые может и предоставляли удобное API, но использовались в каких-то простейших целях, для которых можно было обойтись стандартными библиотеками языка java.  И далее я занялся тем, что начал “выпиливать” эти библиотеки, заменяя их на свои. В итоге получилась сильно(реально сильно) упрощенная версия, которую просто поддерживать, так как всё “на виду”. Было убрано порядка 10 ненужных зависимостей, сборка упростилась и ускорилась. Многие , после прочтения вышесказанного могут сказать : “Ну что, молодец! Крутой!”. Но суть данных слов не в этом.

Суть проблемы заключается в том, что многие специалисты по автоматизации(и скажу по честному - многие программисты) используют сторонние библиотеки, причем не оптимально, на 1-2%, подключая их к своим проектам, не особо разбираясь, как они работают. В итоге это приводит во-первых - к разрастанию количества зависимостей разрабатываемого модуля от других компонентов, а во-вторых (и такой случай встречается очень часто) - к ошибкам из за противоречий между подключаемыми модулями, возникающими по причине несостыковки в версиях используемых уже внутри них самих зависимостей, отчего часто возникают проблемы при сборке пректа, и в-третьих - ухудшается понимание, чего же происходит реально в коде, так как зачастую, при использовании сторонних библиотек особо не разбираются, а что же они реально внутри делают, ограничиваясь мыслью: "Она выполняет то, что мне нужно и мне всё равно, как"

Конечно, изобретать свой велосипед тоже не самый правильный вариант, но цель данного раздела в том, чтобы пояснить, что нужно всегда очень продуманно подходить к тому, что вы используете в своей разработке, а не идти на поводу у моды. Главное - понимать, что происходит у вас в программе и что же реально делает каждый ее модуль. Думайте и разбирайтесь! Это повысит вашу ценность как специалиста и поставит вас выше, чем те, кто считает, что использование того или иного "удобного" решения считается "хорошим тоном", хотя почему это так - объяснить не могут. В основном - из за незнания внутренней структуры этого самого решения.

Советы, как и раньше, к вышеописанному:

  1. Совет 1: не используйте библиотеки, которые не знаете, как реализованы внутри и, при желании и наличии времени, не можете написать сами. Если вы реально разобрались, что именно этот модуль подходит вам для ваших целей, вы поняли, что он будет использован оптимально(а не его 1-2%), то есть большая часть его функциональности будет вами использована , и для создания собственного решения, реализующего ту же функциональность , вами будет потрачено много времени и строчек кода - тогда берите и используйте. В любом случае - перед тем, как подключать что-то стороннее - подумайте о целесообразности этого решения и подумайте, не проще ли сделать всё своими руками? Это, зачастую, упорщает разрабатываемое решение и делает его более легковесным, и позволяет вам развиваться, как специалисту, изучая различные стандартные, входящие в базовое SDK(я рассматриваю это всё на примере языка Java), библиотеки.
  2. Совет 2: старайтесь накапливать как можно больше своих, удобных и проверенных временем разработок и библиотек. Это упростит вам жизнь при написании и поддержке кода и ускорит время, затрачиваемое на поиск нужного вам решения в интернете.
  3. Совет 3: не бойтесь тратить время на то, чтобы досканально разобраться в том, что вы используете. Это возможно покажется дубликатом первого совета, но это очень важно понимать.

Ошибка N4: много настроек

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

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

Ошибка №5: чрезмерная оптимизация

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

Не так давно у нас в компании появился новый сотрудник, которому были переданы на поддержку эти тесты. И он решил переделать их используя достаточно известный подход - data driven testing (думаю если кому-то будет интересно - можно почитать более подробно о нем в интернете). Размер тестов, по сути, сократился до одной таблицы и нескольких шагов. На первый взгляд удобно. Но, если вдуматься поглубже - это не так. В результате этих переделок потерялся ряд очень важных моментов: стало сложно прогонять тесты “по отдельности”, для локализации каких-то неожиданных ошибок; разобраться, что именно делал какой тест (так как тесты по сути стали набором данных, в отличии от старых, где каждый тест имел хорошее понятное название) стало очень сложно, теперь необходимо было просматривать кучу логов для выяснения причины ошибки; отчеты стали непонятными; дополнение и расширение шагов стало намного сложнее.

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

Итоги по ошибкам

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

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

 

Архитектура в автоматических тестах

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

Итак. Сам по себе процесс написания автотестов(я имею в виду момент непосредственного преобразования рукописных тестов в программы) - не сложен, тем более если у вас есть какой-то опыт в этом деле, или же просто в написании кода, знании алгоритмов. Лично по мне - он еще и скучен. Но есть одна фаза , которой нужно уделять основное внимание, а именно - проектирование архитектуры. К сожалению, часто встречаешь ситуации, когда складывается ощущение, что ее вообще не было. И в итоге это приводит к тому, что тратится много ценного времени на вещи, которые можно было бы не делать. А именно - лишние часы и дни на ненужные переделки при добавлении и модификации тестов при изменении тестируемого ПО. Что же такое архитектура? Это, по сути своей, структура разрабатываемой программы,  описание того, как внутри устроены компоненты и как они друг с другом взаимодействуют. Кто-то может подумать , мол это же просто автотесты, зачем тут такие сложные процессы, проектирование архитектуры. Но это очень большое заблуждение.

Что я понимаю под процессом проектирования архитектуры в автоматизации тестирования:

  1. принятие решения - нужны ли для данного компонента автоматические тесты? Или трудозатраты на их написание и последующую поддержку будут несоизмеримы с ручным прогоном? Важнейшая фаза, от которой зависит то, как будет выстроен процесс в тестировании компонента в дальнейшем.
  2. если размышления в пункте 1 привели к решению о написании автотестов , то следующим действием является продумывание того, какой использовать инструмент для их написания.
  3. детальное проектирование проекта : как будут устроены сами тесты? Какие модули будут выделены в фреймворк? Как будет устроено взаимодействие с тестируемым компонентом, с автотестами других команд? Как это все будет запускаться, интегрироваться с системами непрерывной интеграции? Как сделать решение удобным для понимания, расширения, изменения? Эту фазу я обычно проделываю на бумажке и стараюсь по возможности обсудить ее еще с кем то, так как чужой взгляд может заметить какие-то ошибки, допущенные мной.
  4. написание прототипа. Этот пункт я тоже считаю одним из составляющих в проектировании архитектуры(хотя можно было бы его отнести к уже следующей фазе - написанию самих тестов, но мне хочется оставить его именно на этом этапе), и постараюсь объяснить - почему. Его создание обычно не требует много времени и трудозатрат, а польза от него - очень большая. Прототип может наглядно продемонстрировать вам (и не только) все плюсы и минусы вашего подхода. Увидев их, вы сможете оперативно принять меры по устранению проблем на очень раннем этапе, когда можно переделать лишь небольшой кусок, потратив значительно меньше времени, чем если эти проблемы будут выявлены на стадии уже созданных тестов.

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

Так же хотелось бы отметить , что после написания тестов НЕОБХОДИМО (пишу большими буквами, так как считаю это просто обязательным пунктом) создать описание ваших автотестов в виде документа или странички на внутреннем информационном портале(wiki или confluence). Оно должно быть по максимуму подробным и понятным любому, кто захочет попытаться разобраться в ваших тестах. В описании я бы выделил несколько основных частей:

  1. постановка задачи - зачем и что было сделано
  2. где взять? Тут обычно приводится ссылка на систему контроля версий с подробной инструкцией, как получить себе актуальную версию тестов. Так же я считаю нужной информацию о том, как получить необходимые доступы в случае их отсутствия с указанием ответственных за это лиц.
  3. как настроить? Подробнейшее описание , какие сконфигурировать ваши тесты , что нужно передать на вход и в каком виде.
  4. как запустить? Локально, удаленно. Ссылка на задачу в системе непрерывной интеграции.
  5. как добавлять тесты? И желательно описание того, как они устроены.

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

Совет №1: вынесение общего кода

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

  1. Ситуация 1: вы в каждый тест добавили код, делающий необходимые действия.
  2. Ситуация 2: вы выносите необходимый функционал в отдельный метод и пользуетесь им в ваших тестах. 

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

Совет №2: тесты - не изменяемая последовательность вызовов

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

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

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

  1. Низкоуровневые библиотеки - модуль, содержащий код , не имеющий отношения ни к какому из тестируемых модулей, вспомогательный. Например - классы для чтения из файлов, посылки различных запросов. Они используются на следующем уровне.
  2. Уровень “хелперов” - модуль, содержащий библиотеки, разделенные уже по своей функциональности в разрезе тестируемых компонент. То есть если ваш фреймворк используется для тестирования нескольких модулей - разделение используемых вами библиотек для тестирования происходит именно на этом уровне. Более наглядно: вам необходимо протестировать какие то независимые компоненты А и Б. Собственно для них и создаются “хелперы” : АХелпер и БХелпер, которые используются на следующем уровне в виде вызовов включенных в них методов.
  3. уровень тестов - непосредственно тесты, которые как раз и являются последовательностью вызовов функций хелперов.

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

Совет №3: интеграционный

Часто в тестах появляется надобность в интеграции с другими тестовыми проектами или компонентами. И если эту часть хорошо не продумать, то в последствии можно получить множество проблем и ошибок при обновлении используемых модулей. Приведу пример. Предположим, у вас есть 20 тестов. В каждом из них в качестве преднастроек вам требуется создавать пользователя. Пользуясь советом №2 ( мы уже научены на своих ошибках), мы выносим код по созданию в отдельный метод (назовем его createCustomer),и просто используем его далее в своих тестах. Теперь предположим, что алгоритм немного поменялся, и для полного создания пользователя требуется еще вызов некоторого подтверждения. Так как у нас уже есть метод createCusomer, мы просто добавляем в него подтверждение и всё. Наши тесты не меняются и работают отлично. Данный пример сейчас выглядит как пособие для совета №2 , скорее.

Теперь давайте предположим, что кейс создания пользователя для вас кто-то уже сделал в своих тестах, и вы просто хотите использовать его у себя, чтобы не делать работу повторно. Теперь метод createCustomer выступает в роли некоторого прокси между вашими тестами и наработками ваших коллег. Удобно, не правда ли. Теперь, если ваш коллега поменял алгоритм создания пользователя в своих тестах, вам только лишь нужно будет подгрузить себе его новые тесты и пользоваться ими. Максимум, что вам может потребоваться - это сделать какие то изменения в createCustomer. Совет кажется очень простым и очевидным, когда вы пишете тесты на каком-то языке программирования, скажем на java. Это достаточно стандартный подход в разработке. Но часто для написания тестов используется какой то тул, и построение архитектуры проекта в нем по такому принципу может очень сильно облегчить жизнь. Сейчас по работе мне часто приходится иметь место с тестированием веб сервисов с помощью soapUi, и там меня эта структура не раз выручала.

Совет №4: делайте удобно

Что можно понимать под удобством в разрезе процесса автоматизации тестирования? Я бы внес сюда несколько пунктов:

  1. основной пункт - удобство в изменении и дополнении тестов. Проектировать архитектуру автотестов нужно таким образом, что бы любые модификации были не долгими, не затрагивали большой части написанного кода и при этом добавление тестов было быстрым, понятным и не требовало больших трудозатрат.
  2. удобство для других. Всегда, повторюсь , всегда думайте о других. Помните, что в любой момент может случиться ситуация, что ваши тесты отдадут кому-то другому и тогда вам придется объяснять какому-то вашему коллеге струтуру ваших тестов. Я описывал эту проблему выше, в разделе “ошибок”, но я хочу еще раз о ней упомянуть. Мнение: ”Я напишу как мне удобно” - одно из самых плохих в автоматизации тестирования. А что если ваши тесты потребуется дополнять человеку, который не так хорош в написании кода, как вы? Как пример из недавнего опыта - нужно было сделать некоторое решение, которое позволяло тестировать компонент, занимающийся сбором данных из разных БД, выполнением над ними каких-то операций и складыванием получившихся результатов в другую базу, которая использовалась для отчетов. Можно было бы просто написать тесты , скажем на java. Но была проблема, что в нашей команде кроме меня из тестирования java знал всего один человек, и то не очень хорошо. По этому нужно было придумать что-то, чем могли бы пользоваться все. И был разработан фреймворк, позволяющий оформлять тесты в виде простых xml файлов, а дальше при запуске некоторого класса все эти файлы разбирались и на их базе формировались уже программные тесты. Интересно для меня(так как надо продумать множество мелочей) - разработать такой подход, удобно для моих коллег. Всё вышесказанное ни в коем случае не стоит воспринимать как хвастовство, а лишь наглядный пример из личного опыта. Старайтесь делать решения, которые будет просто использовать другим.
  3. удобство интеграции. Тоже один из важнейших пунктов. Всегда думайте, насколько удобно будет интегрироваться с вашими тестами и  не сложно ли будет их автоматически запускать и настраивать в какой-либо continious integration системе.

Совет №5: делайте просто

Очень часто, просматривая автотесты своих коллег(и не только) , сталкивался с очень распространенной ошибкой - всё слишком усложнено. Можно сделать то же самое, но намного проще и понятнее. Помните, что хороший специалист по автоматизации - это не тот, кто может написать много тестов , используя кучу библиотек, а тот, кто сделает это понятно, оптимально и удобно, и не только для себя, но и для всех. Тут наверное хочется объединить в понятие “просто” еще одно слово - “понятно”. Тесты(а так же то, что под ними лежит, фреймворк) должны быть предельно понятными. Достигается это как культурой разработки, так и ,например, такой банальной вещью, как комментарии. Да, очень и очень часто встречаешь фреймворки, в которых код никак не описан. Если вы используете java - всегда пишите javadoc, используете какой-то инструмент - пишите readme или используйте аналог javadoc’ов, встроенный в тул(зачастую такая возможность есть).

Так же достаточно распространенная практика, которую лично я считаю пагубной  - использование сложных и ненужных технологий и фреймворков. Зачем подключать к своему проекту что-то, что делали не вы, в чем вы не сильно уверены, и что может в любой момент отказаться работать? Только ради “дани моде”? Если вы от кого-либо услышите объяснение, что использование какой-то технологии, инструмента или подхода к написанию является “правилом хорошего тона” - сразу советую насторожиться и усомниться в квалификации человека, который вам это говорит. В большом количестве случаев никакой теоретической базы доказательств под этими словами нету. Часто такие “хорошие” разработки в реальной жизни оказываются не такими уже и “хорошими”. Основной вопрос, который я при этом задаю коллегам( и себе) : “Зачем подключать-то, что может вызывать ошибку? Решение может упростить нам, скажем, написание тестов на 5%, но зато принести целую кучу проблем в будущем”. Лишние прослойки ( а зачастую именно в качестве них используются сторонние разработки) лишь являются лишними “черными ящиками”, которые могут сильно ухудшить качество и уменьшить простоту и понимание написанных тестов.

Совет №6: думайте шире

Так же часто, при общении с коллегами, на следующую проблему: человек разрабатывает какой то модуль, который будет использовать не только он и , с какой то долей вероятности, он будет редко меняться. И он допускает достаточно типичные ошибки :
1)тесты разрабатываются без заделов на будущее. Скорее всего это происходит потому, что в нужный момент человек сам себе не задал вопрос : “А что если всё таки что-то поменяется? Что я тогда буду делать?”. В нашем динамично развивающемся мире может произойти всякое.
2)разрабатываемое решение(если пишется некоторый фреймворк) затачивается под определенный инструмент, а о возможностях его использования в других - забывается. Может стоит лучше расширить функциональность фреймворка, но при этом очень сильно упростить процесс интеграции с ним любого возможного тула? Вот очень важный вопрос, который необходимо себе задавать.

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

Итоги по советам

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

 

Роль архитектуры

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

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


Для чего же была эта статья?

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

В данном материале я ни в коем случае не хотел никак хвастаться какими-то удачами в своей работе. Примеры я приводил лишь для наглядности. Эта статья хорошо подойдет как новичкам в сложном процессе автоматизации тестирования, так и “опытным бойцам”, написавшим множество фреймворков. Не стоит рассматривать ее как свод правил, которым обязательно стоит придерживаться. Это как пища для ума, кому-то может показаться не очень, а кто-то прочитав почерпнет для себя много полезной информации. Всё вышеописанное я понял исходя из своего достаточно большого опыта в IT, и теперь хочется поделиться с ним другими. Ведь не даром говорят: “Учиться лучше всего на чужих ошибках”.

16 лайков

Да, всё чётко. Ничего нового, но собрано в одном месте. :)

Я бы сказал, что отчасти эта цель и приследовалась - собрать все мысли в одном месте. 

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

Стоит еще оставить эту презентацию

4 лайка

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

1 лайк

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