Автоматизированное тестирование автотестов

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

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

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

Теперь рассмотрим, что и как мы можем автоматически протестировать:

  • Непосредственно автотесты - динамически такие компоненты проверяются непосредственно во время тестовых запусков, что не выделяет тестирование автоматизированных решений из контекста автоматизированного тестирования, для которого это тесты предназначены. Проще говоря, наиболее эффективный способ проверить работоспособность автотестов - это выполнить эти автотесты.
  • Вспомогательные классы/функции/методы - поскольку подобные компоненты представляют собой некоторый программный код, то ничто не мешает применить для них традиционные практики white-box тестирования.
  • Оконные объекты - этот тип компонентов специфичен для автоматизированного тестирования, в частности GUI-level тестирования и один из наиболее критичных к изменениям тестируемого приложения. Поэтому, для тестирования подобных компонент следует выработать некоторый workflow-сценарий, который затронет все ( или хотя бы просто большинство ) оконных объектов

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

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

{syntaxhighlighter brush: python;fontsize: 100; first-line: 1; }[ ] //******************************************************************************* [+] //* Function: VOID CheckWinDecl(WINDOW wWin,LIST OF WINDOW lwControls optional) [ ] //* Description: Performs frame checking for existence. All controls are searched [ ] //* according to their declaration in frame. The output allows to [ ] //* see which window currently doesn't exist [ ] //* Arguments: [ ] //* WINDOW wWin - window to check controls in [ ] //* LIST OF WINDOW lwControls - list of controls to be checked in window [ ] //* Return Values: none [ ] //* Related: CheckPropertyManyWindows [ ] //********************************************************************** [+] VOID CheckWinDecl(WINDOW wWin,LIST OF WINDOW lwControls optional) [ ] [ ] WINDOW wCtrl [ ] [+] if( !wWin.Exists() ) [ ] LogError("{wWin} : not found") [ ] return [ ] Print( "{wWin} : OK" ) [ ] [+] if( !IsSet(lwControls) || IsNull(lwControls) || lwControls == {} ) [ ] lwControls = WindowChildren(wWin) [ ] [+] for each wCtrl in lwControls [ ] CheckWinDecl(wCtrl){/syntaxhighlighter}

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

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

P.S.: "автоматизированные тесты автоматизированных тестов" тоже можно тестировать автоматически, процесс контроля качества рекурсивен. Но здесь возникает уже вопрос целесообразности. Во-первых, это время. Во-вторых, за большими объемами автотестов автотестов автоматическое тестирование можно и не успеть провести. В-третьих, поскольку код автотестов изначально проще кода тестируемого приложения, то код "автотестов автотестов" еще проще. Рано или поздно код станет настолько простым и плоским, что его работоспособность достаточно будет проверить при помощи code-review и непосредственных запусков

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

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

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

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

В целом картина получается такая (на примере модного треугольника качества Кона, не сексолога, конечно):

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

- серединка - это интеграционные тесты (линейный запуск этих тестов долог, постепенно готовлюсь к переходу в облако)

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

 

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