Статья рассказывает о нетрадиционном, но полезном виде тестов, а также подводит итоги семилетней работы в разработке тестов.
Зачем нужны компонентные тесты?
Ведь есть, скажем, юнит-тесты, которые подробно тестируют потроха компонентов. Они досконально проверяют, что компонент работает в соответствии с замыслом разработчика. Но часто это проверка "пуговиц", а не того, как сидит костюм в целом. И не всегда поведение, задуманное программистом, совпадает с тем что хотел заказчик.
А еще есть, например, приемочные тесты. И они устраняют все указанные недостатки. Но, к сожалению, вносят новые. Они медленные, часто нестабильные, и обычно ручные. При этом они только свидетельствуют о проблеме, но не локализуют ее.
Очевидно, что напрашивается необходимость промежуточных тестов, которые станут золотой серединой между тестами модульными и приемочными. Этой серединой могут стать компонентные тесты.
Что такое компонентные тесты?
Это тесты на публичный API компонента. Соответственно, они пишутся на том же языке, что и компонент. Задача тестов:
проверить соответствие компонента своим контрактам
проверить следование требованиям
Последнее особенно важно, т.к. юнит-тесты обычно пишутся исходя из ожиданий разработчика, а здесь необходимо проверить ожидания клиентов.
Очевидно, что компонентные тесты имеют смысл, когда у вас есть выделенные компоненты с обширным интерфейсом. Например, динамическая библиотека или COM-объект. Тогда компонентные тесты дадут максимальный эффект.
Плюсы к-тестов:
Придают стабильность разработке. Очевидно, что всесторонняя проверка публичных интерфейсов позволяет содержать компонент в более-менее работоспособном виде.
Точно локализуют проблемы. Даже если сам компонентный тест достаточно общий, его всегда можно отладить вглубь, быстро добравшись до боевого кода. При этом тестовое окружение будет минимальным и автоматически настроенным.
Ускоряют разработку при критичном кол-ве разработчиков. Известно, что программисты — они как лошади. Если одна лошадь тянет повозку с мощностью 1 л. с., то восемь лошадей тянут с мощностью всего лишь около 4 л. с. Так и добавление еще одного разработчика в команду (особенно в конце проекта) часто не только не ускоряет, но и замедляет его. Тогда как добавление разработчика компонентных тестов всегда идет в плюс, поскольку он действует относительно независимо от команды: делает (по сути внешние) тесты, ускоряет сборку билда, оптимизирует сборочные файлы etc.
Проясняют (а затем проверяют) требования заказчика. Поскольку разработчик компонентных тестов не завязан на реализацию, он меньше подвержен эффекту "я сам знаю как надо". В случае неоднозначного требования обычный разработчик склонен делать как удобнее (интереснее, быстрее, легче). Тогда как кт-разработчик в этом случае склонен уточнять, что именно ожидается заказчиком.
Относительно стабильные и относительно быстрые (в сравнении с ручными тестами и автоматизированными тестами через пользовательский интерфейс).
Минусы к-тестов:
Время на разработку и поддержку. Очевидно, что альфа-версия продукта, если часть времени потрачена на написание компонентных тестов, появится позднее. Будет ли выигрыш в целом? Выйдет ли релиз раньше? Это хороший вопрос. Мое мнение: при разработке с компонентными тестами релиз выйдет примерно в те же сроки. Но — с меньшими авралами и более предсказуемо по срокам. Время разработки закономерно увеличится, время багфикса и стабилизации сократится. Поскольку вторая часть значительно менее предсказуема, ее сокращение благотворно повлияет на количество переработок и нервотрепки. Однако это только мой опыт, а реальность может оказаться иной. Вполне возможно что время, выделенное на компонентные тесты, окажется бездарно потрачено на дублирование существующих юнит-тестов.
Меньшая взаимозаменяемость. Разделение ролей повышает эффективность, но снижает взаимозаменяемость. Разработчики редко горят желанием лезть вглубь компонентных тестов, которые могут иметь (или имитировать) достаточно сложное окружение. Разработчики компонентных тестов далеко не так хорошо знают боевой код, чтобы с легкостью его править.
Раздражающее дублирование. При хороших юнит-тестах, компонентные тесты часто оказываются по большей части избыточными. Это и раздражает, и ставит под сомнение их необходимость. Согласование планов на юнит и компонентное тестирование помогает, но полностью устранить дублирование обычно не удается.
Необходимость следовать правильному workflow. При получении требований задача должна ставиться одновременно на разработчика и разработчика тестов. Тогда они заканчивают работу примерно одновременно. Компонент прогоняется через тесты, ошибки оперативно ловятся и правятся, и на выход уходит более-менее готовый продукт. В этом случае выгода от компонентных тестов максимальна. Но часто бывает, что сроки давно профуканы, все брошены писать только код, и компонент отправляют в ручное тестирование без тестов. А уже потом приглашают разработчика тестов — мол нехорошо, что код без тестов выпущен, надо бы их дописать. В этом случае большинство багов находится ручными тестировщиками, разработчики делают исправления вслепую (либо сами тестируют правки руками), а написанные постфактум тесты находят лишь незначительное количество ошибок (что отрицательно влияет на мораль их писателя). Такое использование компонентных тестов в лучшем случае бесполезно, а застраховаться от этого тяжело.
Мало подходящих людей. Разработчик компонентных тестов должен, с одной стороны, уметь писать код на языке компонента (а это, например, C++). Причем, если окружение для запуска компонента обширное, код может быть достаточно сложный. А с другой стороны, уметь скрупулезно тестировать чужую работу. Таких людей не очень много, и они идут обычно сразу в разработчики. Но все-таки такие люди есть, и о них — следующая часть.
Резюме 1
Компонентные тесты хороши, но только если для них у вас есть все условия: широкий публичный API, правильный workflow, и подходящие люди в команде.
Каково быть SDET'ом?
Очевидно, что SDET — Software Development Engineer in Test — идеальный кандидат на написание компонентных тестов. Он умеет писать код, и умеет мыслить тестами. Он же обеспечивает второе мнение, что тоже повышает качество тестов и кода. Все это звучит интересно и заманчиво — возможно, вам уже хочется им быть. Здесь я тезисно расскажу, чем работа SDET'а отличается от работы чистого разработчика.
Плюсы работы SDET'ом:
Новый код. Почти всегда SDET пишет тесты с нуля. И достаточно часто с нуля пишется окружение. Это очень приятно и дает большой простор для творчества.
Низкая зависимость от legacy-кода. Каким был ужасным не был боевой код, тесты к нему могут быть сделаны грамотно и красиво. Конечно плохо спроектированный код порождает некрасивые тесты, но все равно они могут быть сделаны на порядок лучше самого кода.
Более частый рефакторинг. Изменения в тестах значительно менее опасны, поэтому на них соглашаются чаще. Это хорошая возможность устроить работу над ошибками и потренироваться в написании чистого кода путем рефакторинга.
Развитие критического мышления. Автотесты — интересный поиск того, как сломать чужой код. Причем поиск не тупой, не просиживанием и тыканьем, а с помощью логики, комбинаторики и умения видеть уязвимости. Плюс единожды созданная проверка дальше будет работать на вас постоянно.
Развитие навыка тестировать код. На тренировках по рукопашному бою часто дают вводные: "теперь работаем только ногами; теперь — только головой". Использование только одного механизма (в нашем случае — автотестов) позволяет отточить его до мастерства.
Меньшее кол-во авралов. SDET'а гораздо меньше дергают на выходные. Им не приходится срочно выезжать на работу на исправление критичных багов. Ну и шанс допустить серьезную ошибку у них значительно ниже.
Минусы работы SDET'ом:
Низкая сложность кодирования. Код тестов обычно все-таки проще боевого кода. Предусловия, вызов боевого кода, постусловия — и так повторить для каждого теста. Код для создания окружения посложнее, но все равно до боевого не дотягивает. Подбор оптимальных алгоритмов, проектирование сложных структур данных, выстраивание иерархий классов — обычно все это проходит мимо разработчика тестов.
Медленнее набирается опыт. Разнообразие и сложность ситуаций, в которые попадает разработчик тестов, значительно меньше. Отказ сборочного конвейера, красные тесты, иногда дамп — вот основной набор, с чем обычно приходится работать. У разработчика витиеватость проблем гораздо выше: начиная от вариаций линковки и сборки, продолжая глюками у конкретного заказчика и хитровыделанными дампами, заканчивая поиском багов в компиляторе и сторонних библиотеках. И не только...
Серьезная разница в стиле тестов с разработчиками. Обычно SDET'ы предпочитают компактные выразительные тесты, которые позволяют создать сложное окружение буквально в несколько строк, и атомарные проверки в стиле равно/не равно (то есть выполнено требование или нет). Иногда доходит и до своего DSL. Просто разработчики предпочитают тесты с тонкой подробной настройкой окружения и многочисленными проверками разных аспектов поведения программы, что приводит к достаточно многострочным тестам. Иногда доходит и до копипасты (что даже лучшие разработчики не считают в данном случае грехом). Тут можно долго дискутировать, как лучше, или даже запилить отдельную статью, но факт — когда разработчик пытается модифицировать тесты SDET'а (или наоборот), то часто это приводит к долгим и мало результативным дискуссиям.
Ниже "грейд". Возможно, в силу более простого кода и меньшей ответственности, но в конечном счете не важно. Просто обычно это так.
Сложнее переход на новую должность. SDET вполне может получить в лоб вопрос: вот вы так долго писали тесты, то есть по сути просто вызывали функции да сравнивали результаты — умеете ли писать настоящий код? Знаете ли все подводные камни языка? Решали ли сложные проблемы? Приходилось ли разбирать витиеватые баги или дампы? Есть ли опыт работы с многопоточностью? Есть ли у вас, в конце концов, амбиции?
Резюме 2
Как человек, который много лет работал разработчиком, потом на несколько лет ушел в SDET'ы, а затем снова вернулся в разработку, могу сказать следующее.
Я очень рекомендую провести SDET'ом хотя бы год-другой. Это весьма полезный опыт для любого разработчика. Но задерживаться там, на мой взгляд, не стоит.