Lecture03 (Лекции в ПДФ)
Описание файла
Файл "Lecture03" внутри архива находится в папке "Лекции в ПДФ". PDF-файл из архива "Лекции в ПДФ", который расположен в категории "". Всё это находится в предмете "тестирование на основе моделей" из 9 семестр (1 семестр магистратуры), которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст из PDF
Тестирование на основе моделейВ. В. КуляминЛекция 3. Критерии полноты тестированияНабор тестов, используемый при тестировании, всегда конечен и, более того, ограниченсоображениями экономической эффективности распределения ресурсов между разнымивидами деятельности при разработке ПО. Поэтому крайне важно строить его так, чтобыиспользуемые тесты проверяли как можно больше разных аспектов функциональностисистемы в как можно большем разнообразии ситуаций. Чтобы систематическим образомперебирать существенно отличающиеся друг от друга ситуации, используют критерииполноты тестирования или критерии адекватности тестирования [1,2]. Тестовый набор,удовлетворяющий заданному критерию полноты, называют полным по этому критерию.Чаще всего для определения критерия полноты некоторые из возможных тестовыхситуаций рассматривают как эквивалентные и определяют количество классовнеэквивалентных тестовых ситуаций, встретившихся или «покрытых» во времятестирования.
Такие критерии полноты называются критериями тестового покрытия [1-3].При этом определяется и числовая метрика тестового покрытия — доля покрытых классовситуаций среди всех возможных. Критерий полноты может использовать различныезначения метрики, например, он может требовать, чтобы полный тестовый набор всегдапокрывал 100% выделенных классов ситуаций, или же считать достаточным покрытие 85%классов ситуаций. Поскольку для одной метрики покрытия можно определить многокритериев полноты, далее речь, чаще всего, идет о различных метриках тестового покрытия.Полноту тестирования можно определять по-разному, но в основе любого критерияполноты лежит представление о возможных ошибках в тестируемой системе.
Различныеспособы классификации ситуаций, отражающие их разнообразие с точки зрениятестирования, перечислены ниже. Каждый из них и любое их подмножество совместно могутиспользоваться для определения метрик тестового покрытия. Классифицировать ситуацииможно следующим образом.•На основе структурных элементов тестируемой системы, которые выполняются илизадействуются в ходе тестирования.•На основе структуры входных данных, используемых во время тестирования.•На основе элементов требований, проверяемых при выполнении тестов.•На основе явно сформулированных предположений об ошибках, выявление которыхдолжны обеспечить тесты.•На основе произвольных моделей устройства или функционирования тестируемойсистемы.Последний вид метрик покрытия является самым общим — все критерии полнотыиспользуют, так или иначе, какие-то модели системы.
Дополняя такую модель некоторымигипотезами о возможных ошибках в системе — в чем именно она может отличаться от этоймодели, мы всегда получим основу для определения метрики тестового покрытия. Первыечетыре вида, однако, выделены, поскольку используемые в них модели имеют четкоопределенную природу — это модели структуры самой системы, модели структуры еевходных данных, модели требований и модели ошибок определенного вида. К пятой группеотносятся метрики, основанные на моделях, не принадлежащих к этим разновидностям.Структурные критерииКритерии полноты тестирования и метрики тестового покрытия, основанные наструктуре тестируемой системы, называются структурными, а тестирование, проводимое сих использованием — структурным тестированием.В основе структурных критериев полноты лежит простая идея: если ошибка находится вкакой-то конструкции кода, в каком-то компоненте тестируемой системы, то выполнив этуконструкцию или заставив работать этот компонент, мы, скорее всего, сможем ееобнаружить.
Соответственно, если в двух ситуациях выполняются одни и те же элементыкода, такая ошибка будет либо проявляться в обеих ситуациях, либо не проявляться ни водной, поэтому их можно объявить эквивалентными и проверять всегда только одну из такихситуаций.Это предположение редко выполняется на практике, однако как эвристика дляопределения метрик тестового покрытия, оно достаточно полезно. Далее для некоторыхконкретных структурных метрик будут приведены примеры простых программ, в которыхвыполнение одной и той же конструкции в некоторых случаях вскрывает ошибку, а внекоторых — нет.Структурные метрики покрытия различаются в зависимости от размера элементовсистемы, используемых при их определении. Можно выделить три уровня структурныхметрик — уровень отдельной функции или отдельного метода класса, уровень компонентаили класса, включающего несколько операций, и уровень подсистемы или системы в целом,в составе которых может быть много компонентов.Вне зависимости от уровня структурные метрики могут быть основаны на информациидвух видов — на информации о передаче управления между разными исполняемымиэлементами системы или на информации об использовании и записи данных.
Метрикипервого типа называются основанными на потоке управления, второго типа — основаннымина потоках данных.Важным достоинством структурных метрик покрытия является возможность ихавтоматизированного вычисления при наличии доступа к коду или схемам архитектурытестируемой системы. Существенным недостатком является отсутствие учета требований —мы можем покрыть все элементы структуры, но не обнаружим, что какое-то требованиепросто забыли реализовать.Структурные критерии на уровне отдельной функцииСтруктурные метрики покрытия для одной функции или метода на основе потокауправления базируются на исполняемых в ходе теста элементах кода этой функции или этогометода.Метрики покрытия на основе потока управленияНаиболее простая из таких метрик — метрика покрытия инструкций (statementcoverage), равная доле выполненных во время тестирования инструкций кода функции поотношению ко всем ее инструкциям. Поскольку в большинстве современных языковпрограммирования принято писать не более одной инструкции в строке, эта метрика чащевсего коррелирует с метрикой покрытия строк исходного кода.
Однако всегда при разговорео строках кода стоит уточнять, насколько они соответствуют инструкциям, потому что частьстрок содержит декларативную, неисполняемую информацию, и информация обинструкциях позволяет точнее оценить ситуацию. В том случае, если часть инструкций недостижима, т.е. не может быть выполнена ни при каких условиях, долю покрытыхинструкций определяют только по отношению ко всем достижимым инструкциям.Рассмотрим следующий пример.1234567int gcd(int a, int b){if(a == 0)return b;if(b == 0)return a;if(a > 0 && b < 0 || a < 0 && b > 0)89101112131415161718192021222324b = -b;while(b != 0){if(b > a && a > 0 || b < a && a < 0){a = b-a;b = b-a;a = a+b;}b = a-b;a = a-b;}return a;}Приведенная функция вычисляет наибольший общий делитель своих аргументов.При вызове этой функции с аргументами 0 и 1 выполняются только инструкции в строках3 и 4.
При вызове с аргументами 1 и 0 будут выполнены строки 3, 5, 6. При вызове саргументами 1 и –2 выполняются строки 3, 5, 7, 8, 10, 12, 14, 15, 16, 19, 20, 23. Такимобразом, набор тестовых данных, состоящий из пар <0, 1>, <1, 0>, <1, –2> обеспечиваетполное покрытие инструкций этой функции.Заметим, что вместо <1, –2> можно было бы использовать два набора аргументов,например, <1, –1> и <1, 2>, первый из которых покрывает инструкцию 8, но не покрывает 14,15, 16, а второй — покрывает эти три инструкции. С точки зрения получаемого покрытия всеравно, какой набор тестовых данных выбрать.
Однако могут быть существенны другиеаспекты, например, время работы тестового набора и удобство анализа результатовтестирования. Время выполнения тестов обычно сокращается при уменьшении ихколичества, но сложный тест, эквивалентный по покрытию нескольким простым, в рядеслучаев может выполняться дольше, чем все они вместе взятые. С точки зрения удобстваанализа результатов, чем проще тесты, тем лучше, поскольку меньше различных факторовприходится рассматривать при локализации ошибки, найденной таким тестом.В примерах, приведенных ниже, обычно используются наиболее компактные полныетестовые наборы для заданной метрики, но на практике всегда стоит рассмотреть вопросыэффективности выполнения тестов и удобства анализа результатов, прежде чем пытатьсяминимизировать их количество.Для обнаружения всех ошибок покрытия 100% инструкций недостаточно. В следующемпримере приведен код функции, которая должна по значению целого числа печатать егопростую характеристику — ноль это, четное или нечетное число, положительное илиотрицательное.
В этом коде пропущена вставка слова «нечетное» в описание нечетныхчисел. Однако тесты с входными данными 0, 2 и –2 дадут 100% покрытия строк и необнаружат никаких ошибок.1234567891011121314String classifier(int n){StringBuffer s = new StringBuffer();if(n == 0)return "ноль";if(n%2 == 0)s.append("четное ");if(n < 0)s.append("отрицательное");elses.append("положительное");151617return s.toString();}Проблема здесь в том, что определенный код ошибочно пропущен, а покрытиеинструкций, естественно, не гарантирует обнаружения пропущенных инструкций, посколькуоно вычисляется только по имеющимся.
Чтобы решить эту проблему, используют покрытиеветвей.Для определения ветвей нужно рассмотреть граф потока управления программы. Графпотока управления для приведенного выше примера изображен ниже. Для пояснений ублоков кода, которые всегда выполняются в одной последовательности, помещенысоответствующие инструкции. Каждый условный оператор (также как и оператор цикла иливыбора) имеет несколько ребер графа, ведущих из него, в соответствии с возможным ходомвыполнения инструкций, то есть определяет разветвление потока управления.
Каждое ребро,выходящее из вершины графа, из которой выходят и другие ребра, называется ветвью.Условному оператору соответствуют два выходящих из его вершины ребра, операторупроверки условия цикла — тоже два (выйти из цикла или нет), а оператору выбора можетсоответствовать много выходящих ребер — по числу указанных вариантов значенийвыражения, по которому осуществляется выбор.StringBuffer s = new StringBuffer();AданетBn == 0Creturn "ноль";данетn%2 == 0Ds.append("четное ");Eдаn < 0нетFs.append("отрицательное");s.append("положительное");return s.toString();Покрыть ветвь означает обеспечить такой ход выполнения инструкций, что послеинструкции ветвления, которая является начальной вершиной этой ветви, выполнитсяинструкция в концевой вершине ветви.