Автоматические модульные тесты при разработке корпоративного ПО
В журнале IT News (№7/2012) опубликована статья ведущего разработчика Андрея Ковалева «Автоматические модульные тесты при разработке корпоративного ПО». Андрей рассказывает, как и когда появилась идея написания автоматических тестов и какие плюсы дает их применение, а также пытается разобраться, почему, несмотря на все положительные эффекты, они так редко используются на практике.
Что произойдет, если в программу поступят данные, которые изначально не планировались? Или в настройках новой версии программы неправильно указан какой-то, казалось бы, не очень важный параметр? Справится ли код с такой ситуацией, выдаст ли программа адекватное сообщение об ошибке или на экране появятся малопонятные символы и последует сбой, парализующий работу пользователя и, возможно, бизнес-процесс?
Пожалуй, наиболее эффективным способом убедиться, что программа работает должным образом, является автоматическое тестирование кода. Оно позволяет гарантировать корректную работу программы не только в типовых, но и в нестандартных ситуациях, а также при последующих изменениях системы (например, при выпуске новых версий). Автоматические тесты особенно актуальны для крупных информационных систем, которые планируется развивать и поддерживать.
Идея написания автоматических модульных тестов (называемых также unit-тестами) при разработке ИС появилась в 70-х годах прошлого века и была впервые реализована с помощью языка программирования Smalltalk. Однако практическое их применение при создании ИС уровня предприятия началось не так давно. Технология в ее современном виде впервые описана в методологии экстремального программирования (Extreme Programming), представленной Кентом Беком (Kent Beck) в 1999 году. Впрочем, идеи автоматизированного тестирования продолжают развиваться и по сей день. Так, например, в гибкой методологии ATDD (Acceptance Test-Driven Development) предполагается переход от модульных (unit) к приемочным (acceptance) автоматическим тестам. Остальные гибкие методологии не настолько радикальны.
В настоящее время автоматические тесты доказали свою эффективность и все чаще применяются при разработке программного обеспечения. Однако на конференции Microsoft «Платформа-2011» произошел любопытный случай. Один из ключевых докладчиков, Дэвид Чеппелл (David Chappell), обратился к залу с просьбой поднять руки тех, в чьих компаниях при разработке ПО используются автоматические модульные тесты. Таковых оказалось лишь несколько человек. Дэвид был очень удивлен и громко воскликнул: «Why?!» (корпорация Microsoft уже с 2005 года предоставляет поддержку автоматических тестов в своих инструментах для разработчика).
Зачем это нужно?
Разработчики часто сталкиваются с ситуацией, когда при изменении какого-либо ключевого алгоритма, используемого в нескольких местах системы, в зависимых частях также могут произойти изменения, в том числе непредсказуемые. Автоматические тесты выявляют возможные ошибки еще на этапе разработки, позволяя модифицировать имеющийся код без риска его «сломать». Поэтому код, «покрытый» тестами, удобен в обслуживании и сопровождении, пригоден для повторного использования и более надежен.
Автоматические тесты экономят время как первоначального тестирования кода разработчиком (открытие форм, ввод данных, ожидание…), так и последующего регрессионного тестирования (благодаря таким тестам, его можно частично автоматизировать, то есть избавиться от избыточности).
Еще одним достоинством автоматического тестирования является то, что оно помогает не только проверять стрессоустойчивость системы, но и определять производительность ее отдельных частей. Кроме того, использование автоматических тестов «вынуждает» разработчика улучшать качество кода: тщательно продумывать архитектуру приложения, разделять слои, отделять логику работы с базой данных от бизнес-логики, использовать преимущества объектно-ориентированного программирования, чаще применять шаблоны проектирования. Существует целая методология разработки «через тестирование» (Test-Driven Development), в соответствии с которой сначала пишется модульный тест, проверяющий ожидаемое поведение, и только после этого — сам код.
А как без них?
В двух компаниях, где я работал раньше, написание автоматических модульных тестов считалось ненужным и не практиковалось. При этом если в одной компании (назовем ее А) существовал целый отдел тестирования, то другая (Б) перекладывала тестирование продукта на конечного пользователя. Стоит признать, что ни первый, ни второй вариант не был особо эффективным.
Так, в компании А процесс разработки и тестирования постоянно затягивался, а количество тестовых случаев (test cases), требующих ручной проверки, росло как снежный ком. Кроме того, эти узкие места приходилось вручную перепроверять в каждой новой версии продукта, отдавать программу обратно на доработку, ждать исправлений, перепроверять заново и т. д. Не стоит забывать и о человеческом факторе: специалист отдела тестирования может банально забыть проверить что-то или понадеяться, что если некий функционал работал в прошлой версии, то будет работать и в новой. А вот компания Б, наоборот, довольно быстро выпускала новые версии продукта, однако, что неудивительно, постоянно получала жалобы от пользователей. Доработка продукта была также очень непростым делом, поскольку никто не мог дать гарантии, что при изменении функциональности какого-нибудь блока построения диаграмм не «сломается» что-то другое.
Общей проблемой обеих компаний было то, что найденные и вроде бы когда-то исправленные ошибки появлялись вновь. Кроме того, нельзя забывать, что при устранении одной ошибки всегда есть риск внести другую, которая проявится позже и в другом месте. Обе эти проблемы успешно решаются с помощью автоматических тестов, которые «отлавливают» повторно возникающие ошибки еще на этапе разработки и позволяют каждый раз после внесения изменений быстро убедиться, что программа работает должным образом.
Не панацея
Безусловно, автоматические тесты не лишены некоторых недостатков. Первый и, наверное, основной из них — дополнительные затраты на написание и поддержку автоматических тестов. Грубо говоря, вместо одной ИС мы получаем две, одна из которых занимается автоматической диагностикой другой. Однако, что любопытно, большая часть времени разработчика уходит отнюдь не на кодирование функционала. Здесь уместна цитата Мартина Фаулера (Martin Fowler), известного автора ряда книг по архитектуре ПО: «Некоторое время уходит на уяснение задачи, еще какая-то его часть — на проектирование, а львиную долю времени занимает отладка»[1].
Действительно, на написание тестов и их поддержку требуется дополнительное время, однако эти затраты, скорее всего, позволят выиграть время впоследствии, при отладке и выявлении ошибок, а если появятся новые требования — то и при расширении системы. Но если не учитывать дальнейшие перспективы, может сложиться впечатление (в том числе и у заказчика), что разработка с использованием автоматических тестов занимает больше времени, чем без тестов, и, соответственно, стоит дороже.
Второй недостаток заключается в том, что автоматические модульные тесты делают разрабатываемую программу несколько более инертной. Это связано с тем, что внесение изменений в систему, «покрытую» автоматическими тестами, занимает больше времени, поскольку, помимо самой системы, их нужно вносить в тесты. Этот фактор может стать существенным, когда заказчик требует сделать в программе «срочные» изменения в очень сжатые сроки (к примеру, за несколько часов).
Третий недостаток автоматических тестов связан с их неспособностью заменить «ручное» тестирование. Действительно, сложно представить автомобиль, который полностью сам себя диагностирует и выдает точное место поломки с рекомендациями.
Однако автоматическое и «ручное» тестирование прекрасно дополняют друг друга. Автоматические тесты уменьшают количество рутинной работы, сокращают общее время регрессионного тестирования и позволяют сосредоточиться на «ручном» тестировании нового функционала. То есть автоматические модульные тесты помогают избавиться от избыточного «ручного» тестирования.
Следует иметь в виду (и это можно с некоторой долей условности назвать четвертым недостатком), что с помощью автоматических тестов нельзя протестировать абсолютно все. Не стоит доходить до абсурда и создавать чрезмерно сложные тесты, иначе на их написание и последующую поддержку уйдет слишком много времени, и они станут действительно невыгодны в плане трудо- и временных затрат.
О невозможности «всеохватного» тестирования пишет и Кент Бек, уже упомянутый нами создатель методологии экстремального программирования: «Протестировать абсолютно все невозможно — для этого тесты должны быть столь же сложными и столь же беззащитными перед ошибками, как и сам код приложения. Ничего не тестировать (в смысле изолированных автоматических тестов) — это самоубийство»[2].
Автоматические тесты давно стали не инновацией, а в некотором роде стандартом при разработке корпоративного ПО. Их использование обеспечивает более высокое качество кода и, соответственно, программного обеспечения в целом, помогая заметно повысить надежность системы. Однако из-за ограничений архитектуры разрабатываемого програмного продукта, а также по причине сложности написания действительно качественных и полезных тестов автоматическое модульное тестирование применяют не все компании, разрабатывающие ПО.
Любые правки этой статьи будут перезаписаны при следующем сеансе репликации. Если у вас есть серьезное замечание по тексту статьи, запишите его в раздел «discussion».
Репликация: База Знаний «Заказных Информ Систем» → «Автоматические модульные тесты при разработке корпоративного ПО»