Статьи

Разработка через тестирование без слез

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

Предупреждение: мои воспоминания о событиях могут отличаться от того, что произошло на самом деле. Ты помнишь, что делал 10 лет назад?

Не проверяйте боль в заднице.

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

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

Тестирование должно проводиться разработчиками постоянно и в процессе разработки.

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

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

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

Тестовая система была совершенно болезненной в использовании. Ранние версии работали в режиме реального времени, с реальными таймаутами. Чтобы проверить, что тональный сигнал готовности воспроизводился только в течение 30 секунд до того, как начались звуковые сигналы снятия трубки, вам пришлось сидеть там и ждать, пока система ничего не сделает, пока не истечет таймер. Был ускоренный режим, где таймеры истекали немедленно. Но даже в скоростном режиме система Java была настолько завалена, что для запуска всего потребовалось бы несколько часов. Пока я был там, они собирали кластер из 100 компьютеров, чтобы выполнить все тесты.

Проблема заключалась в том, что все тесты были системного уровня. Программирование на Java в конечном итоге приводит к системе, которую я называю object-soup : больше абстракции может показаться лучше, но это приводит к большему количеству классов, и больше классов имеют больше ссылок друг на друга. Система стала настолько сложной, что невозможно было восстановить состояние до середины пятистороннего вызова, фактически не выполнив всю настройку вызова. Каждый должен был быть настроен для каждого теста. Модульное тестирование отдельного класса было бессмысленным, но было невозможно отделить функцию от остальной части системы.

Вы не можете проверить GUI

В 1999 году флагманским продуктом Corel стал DRAW! Комплект векторной графики. Насколько я помню, тестирование было полностью ручным. Конечно, разработчики должны были как можно больше тестировать свои изменения, но CorelDRAW — это огромная программа с тысячами функций и режимов работы. После небольшого беглого тестирования мы зарегистрировали изменения в Visual Source Safe и ожидали ошибок от бета-тестеров. Когда ошибка была отправлена, специалист по тестированию моей команды, Мона, воспроизвел ее и создал отчет о проблеме.

Система была явно сломана. У нас было около 100 000 открытых ошибок, и даже некоторые из более серьезных ошибок (копирование текста с определенными стилями маркеров приводит к сбою.) Были исправлены в нескольких версиях.

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

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

Примечание: большинство людей перестают читать на этом этапе.

Три правила для разработки через тестирование

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

  • Тестовая система должна поддерживать разработчика, а не наоборот.
  • Разработчик должен создать новый тест менее чем за 10 минут.
  • Набор тестов должен быть способен выполнять сотни тестов менее чем за 5 минут.

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

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

Наконец, набор тестов должен быть быстрым. Вы захотите, чтобы ваша автоматизированная система сборки запускала тесты после каждых нескольких изменений. Если подумать, разработчик может вносить в среднем одно или два изменения в день. Если у вас 100 разработчиков, у вас будет от 100 до 200 изменений в день. Разработчики также должны иметь возможность запускать тесты на своем рабочем столе. Если прохождение регрессионных тестов является обязательным, прежде чем вносить изменения, то запуск может занять всего несколько минут, чтобы у вас не было разработчиков, которые уезжали на трехчасовой обед, проверяли свои запасы или дрались на мечах.

 

Пример средства импорта файлов

Допустим, мы несем ответственность за написание импортеров файлов для текстового процессора. Мы исправляем ошибку в импортере файлов для формата Microsoft DOC. Входные данные представляют собой файл .DOC, а выходные данные представляют собой последовательность вызовов функций, которые изменяют документ. Поэтому, когда мы читаем некоторый текст, мы вызываем document.addText (), а когда мы получаем новый шрифт, мы вызываем document.setFont () и т. Д.

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

called addText("This document is copyright")
called setFont("Symbol", 10)
called addText("c")
called setFont("Times New Roman", 10 )
called addText("1995")

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

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

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

Теперь мы можем переписывать вещи без страха. Мы можем переписать импортеры файлов с нуля и убедиться, что они одинаково работают с одинаковыми документами. Мы также можем переписать остальную часть системы, если интерфейс addText () и setFont () все еще работает одинаково.

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

В заключении

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

Источник:  http://stevehanov.ca/blog/index.php?id=66