Оптимизация процесса тестирования по С с использованием эвристического анализа тестового покрытия кода (1187409), страница 2
Текст из файла (страница 2)
Стандартизация интерфейсовТестирование подобным методом позволяет гарантировать совместимостьмодулейдругсдругом,чтоупрощаетпоследующийпроцесс[11]интеграционного тестирования.1.1.4. РефакторингС помощью аналитических результатов тестов, таких как покрытие иливремена работы отдельных функций, разработчик может обнаруживать“мертвый” или неоптимальный код.91.1.5. Разработка через тестирование (TDD)Заключается в инвертировании процесса разработки модуля.
Сначала, потребованиям, происходит создание тестов. В дальнейшем, они выступают“каркасом” для настоящего кода, который постепенно проходит все тесты.В дальнейшем проводится рефакторинг написанного кода, защищенноготестами от регрессов. Необходимо указать, что важной частью разработкичерез тестирование является сепарирование тестов друг от друга - принаписании нового теста в первую очередь проверяется, что он непроходит. Это гарантирует покрытие каждым тестом конкретно своего[6]случая.1.2.
НедостаткиКсожалению,юнит-тестированиеневсегдадаеттольколишьпреимущества. К недостаткам и сложностям данного метода можноотнести следующее.1.2.1. ГромоздкостьДопустим, тестируемый код состоит из последовательного принятиярешенийвзависимостиотзначенийбулевыхпеременных.Комбинаторным путем легко можно посчитать количество тестов,требующихся для его покрытия. А в случае комплексных проверокрезультатов,атакжеучитывая необходимость имитации работысторонних модулей, объем тестирующего кода может превосходитьтестируемый разы, а иногда и в десятки раз.1.2.2. Зависимость от культуры разработкиСтрогие правила, которым необходимо следовать при использованииданной технологии, не всегда являются либо понятными разработчику,либо приемлемыми с точки зрения компании.
Требуется отслеживаниеданных обо всех тестах, использование системы контроля версий, а также10отсутствие хаотического изменения требований к модулю. В противномслучае преимущества данного метода не будут проявляться, а самотестирование лишь замедлит разработку.1.2.3. Низкая полезность в отсутствие других этапов тестированияНе смотря на то, что, с точки зрения требований, модуль будет работатькорректно, при отсутствии более высокоуровневых проверок тестируемыйкод может оказаться бесполезным. Любой проект, для которого актуальнотестирование, состоит из многих частей, и именно результат ихсовместной работы интересует конечного потребителя, а не корректностьработы из по-отдельности.1.2.4.
Устаревание и неверная работа объектов-заглушекОдним из принципов юнит-тестирования является сосредоточениевнимания конкретного теста на конкретной части кода. Для этогоразработчик создает примитивные объекты-заглушки, подменяя имивызовы других модулей. Фактически, идет дублирование кода, чтоприводит к ситуации, когда разработчики вносят изменения в реальныйкод, но забывают про изменение работы имитации. Последствиями этогоявляются ошибки интеграции и то, что код завязывается на неверныйфункционал.1.3. ВыводТак как любой процесс разработки проходит в условиях ограниченностиресурсов, описанные выше проблемы имеют тенденцию проявляться влюбых более-менее крупных проектах. Они неизбежны, однако отказ оттестирования приводит к куда более катастрофическим последствиям.112.
Покрытие кодаВ данном исследовании нас интересует такая характеристика, какпокрытие (coverage) кода. Покрытие определяется как множествозатронутых набором тестов элементов кода. Анализ покрытия позволяет:● Обнаружить части программы, не затронутые тестами● Создать дополнительные тесты, чтобы повысить покрытие● Получить количественные характеристики покрытия, оцениваятаким образом качество тестов● Обнаружить пересечения тестируемых случаев, что позволяетизбавиться от лишних тестов● Обнаружить избыточные условия● Выявить неадекватность требований к программеВажно помнить, что покрытие определяет качество набора тестов, но нетестируемого кода.2.1.
МетрикиСуществует множество метрик, различным способом характеризующихуровень покрытия. Опишем их, от более простых к более комплексным.2.1.1. Построчное покрытие (Statement Coverage)Для обеспечения полного покрытия программного кода на данном уровненеобходимо, чтобы в результате выполнения тестов каждый оператор былвыполнен хотя бы один раз. Наиболее примитивный вариант оценки полноепокрытиеозначает,что из всех возможных логическихкомбинаций тесты смогли предоставить как минимум одну.
Описанныйниже пример будет иметь полное построчное покрытие, но будетпропущена проверка существенной части логики, а именно ошибка припередаче Falseв качестве параметра функции.12deffoo(condition:bool):ifcondition:result =TruereturnresultassertTrue(foo(True))2.1.2. Покрытие ветвей (Decision Coverage)Данный метод ориентирован на покрытие всех возможных логическихпутей исполнения тестируемой программы. Учитываются также неописанные варианты, как, например, отсутствие else в коде выше.
Дляполного его покрытия потребуется уже два теста (пусть код и работаетнекорректно):assertTrue(foo(True))assertRaises(UnboundLocalError,foo,False)Данный случай, однако, не предоставляет верного анализа логическихусловий.
Для демонстрации этого, модифицируем пример:deffoo(condition_one:bool,condition_two:bool):ifcondition_one and(condition_two orimportant_code_call()):returnTrueelse:returnFalseassertTrue(foo(True,True))assertFalse(foo(False,False))Покрытие будет полным, однако important_code_call так и не будетвызвана.132.1.3. Покрытие по условиям (Condition Coverage)Данный метод требует для полного покрытия предоставление всехвозможных значений параметров для логического условия. Однакопринятие всех возможных значений самим условием не требуется.2.1.4. Покрытие по веткам и условиям (C/D Coverage)Сочетание двух предыдущих методов.
Но и это не позволяет полностью[13]протестировать правильность логической функции.2.1.5. Покрытие по всем условиям (Multiple Condition Coverage)По настоящему “полное” покрытие, требует предоставления всехвозможных наборов значений элементов логического условия. Данныйметод считается избыточным и проверяется редко. Зависит от структурыусловий - для разных условий с одинаковым количеством операторов[13]может понадобиться разное количество тестов.2.1.6. MC/DC (Modified Condition/Decision Coverage)Данный метод разработан компанией Boeing и используется дляуменьшения количества тестов при тестировании логических условий.Полное покрытие достигается следующим образом:● Логическое условие должно принимать все возможные значения● Каждая компонента условия должна принимать все возможныезначения хотя бы раз● Должно быть показано независимое влияние каждой компоненты назначение при фиксации остальных компонентНе смотря на то, что количество тестов для получения полного покрытияпо этой метрике остается большим, оно все-равно значительно меньше,[13]чем использование всех возможных проверок.143.
Проблема длительности тестового цикла3.1. Описание проблемыВ погоне за полным покрытием, а также в соответствии с требованиямимодульного тестирования, разработчики производят огромное количествотестов. А если учитывать то, что в реальности проявляются описанныеранее проблемы, это число растет экспоненциально. В итоге даже всредних проектах, зачастую, разработчику приходится часами ждатьпроверки единственной правки кода, чтобы продолжить над ним работу.Проявление ошибок в самих тестах, изменение требований, даун-таймтестирующих машин - все это приводит к торможению процесса.Подобныепримерыубеждаютнекоторыхразработчиковвообщеотказываться от тестов, а менеджеров - закладываться на меньшуюэффективность команды в процессе планирования.3.2. Варианты решенияЕсть два очевидных пути решения данной проблемы - это подход состороны оптимизации кода и со стороны оптимизации тестов.3.2.1. Работа с кодовой базойРефакторинг, в ходе которого достигается большая обособленностьотдельных компонент.
Разбиение больших частей на малые, избавление отбесполезного кода и относящихся к нему тестов - все это существенноускоряет и упрощает этап юнит-тестирования. Однако данный подход неявляется универсальным. Более того, чем серьезнее проблемы, связанныесо сложностью кода, тем труднее и затратнее проводить рефакторинг. Внекоторых ситуациях полная переработка с нуля с учетом ошибок и опыта,полученного в ходе работы над предыдущим поколением продукта, будетдаже более выгодной, так как увеличивает возможности для дальнейшегоразвития проекта.
К сожалению, данный способ является наиболее15затратным и часто неприемлемым, особенно если разработчик имеет делосширокораспространенным продуктом, имеющим одновременно[4]несколько версий, используемых конечным потребителем.3.2.2. Оптимизация тестовДажевнаиболеезапущенныхслучаяхсуществуетвозможностьраздельного тестирования.