тестирование. Тестир. инф. систем Метод материалы. Сыркин Илья Сергеевич Тестирование информационных систем методические указания
Скачать 2.91 Mb.
|
Контрольные вопросы 1. Расскажите порядок создания модульного теста в VS. 2. Что такое рефакторинг кода? 3. Как провести рефакторинг, используя модульные тесты? 13 2. Практическое занятие №2. Обработка исключительных ситуаций Целью работы является изучение способов обработки исклю- чительных ситуаций. Результатом практической работы является отчет, в котором должны быть приведены исходные коды програм- мы, демонстрирующей умение обрабатывать исключения. Для выполнения практической работы № 2 студент должен изучить приведенный ниже теоретический материал. Отчет сдается в распечатанном и электронном (файл Word) видах. 2.1. Основы обработки исключений Далеко не всегда ошибки случаются по вине того, кто кодиру- ет приложение. Иногда приложение генерирует ошибку из-за дей- ствий конечного пользователя, или же ошибка вызвана контекстом среды, в которой выполняется код. В любом случае вы всегда должны ожидать возникновения ошибок в своих приложениях и проводить кодирование в соответствии с этими ожиданиями. В .NET Framework предусмотрена развитая система обработки ошибок. Механизм обработки ошибок C# позволяет закодировать пользовательскую обработку для каждого типа ошибочных усло- вий, а также отделить код, потенциально порождающий ошибки, от кода, обрабатывающего их. Что бы ни служило причиной проблем, в конечном итоге при- ложение начинает работать не так, как ожидается. Прежде чем пе- реходить к рассмотрению структурированной обработки исключе- ний, давайте сначала ознакомимся с тремя наиболее часто приме- няемыми для описания аномалий терминами: Программные ошибки (bugs) Так обычно называются ошибки, которые допускает програм- мист. Например, предположим, что приложение создается с помо- щью неуправляемого языка С++. Если динамически выделяемая память не освобождается, что чревато утечкой памяти, появляется программная ошибка. Пользовательские ошибки (user errors) В отличие от программных ошибок, пользовательские ошибки обычно возникают из-за тех, кто запускает приложение, а не тех, кто его создает. Например, ввод конечным пользователем в тексто- вом поле неправильно оформленной строки может привести к гене- 14 рации ошибки подобного рода, если в коде не была предусмотрена возможность обработки некорректного ввода. Исключения (exceptions) Исключениями, или исключительными ситуациями, обычно называются аномалии, которые могут возникать во время выполне- ния и которые трудно, а порой и вообще невозможно, предусмот- реть во время программирования приложения. К числу таких воз- можных исключений относятся попытки подключения к базе дан- ных, которой больше не существует, попытки открытия поврежден- ного файла или попытки установки связи с машиной, которая в те- кущий момент находится в автономном режиме. В каждом из этих случаев программист (и конечный пользователь) мало что может сделать с подобными «исключительными» обстоятельствами. По приведенным выше описаниям должно стать понятно, что структурированная обработка исключений в .NET представляет со- бой методику, предназначенную для работы с исключениями, кото- рые могут возникать на этапе выполнения. Даже в случае про- граммных и пользовательских ошибок, которые ускользнули от глаз программиста, однако, CLR будет часто автоматически генериро- вать соответствующее исключение с описанием текущей проблемы. В библиотеках базовых классов .NET определено множество раз- личных исключений, таких как FormatException, IndexOutOfRange Exception, FileNotFoundException, ArgumentOutOfRangeException и т. д. В терминологии .NET под «исключением» подразумеваются программные ошибки, пользовательские ошибки и ошибки времени выполнения. Прежде чем погружаться в детали, давайте посмотрим, какую роль играет структурированная обработка исключений, и чем она отличается от традиционных методик обработки ошибок. 2.2. Роль обработки исключений в .NET До появления .NET обработка ошибок в среде операционной системы Windows представляла собой весьма запутанную смесь технологий. Многие программисты включали собственную логику обработки ошибок в контекст интересующего приложения. Напри- мер, команда разработчиков могла определять набор числовых кон- стант для представления известных сбойных ситуаций и затем при- менять эти константы в качестве возвращаемых значений методов. 15 Помимо приемов, изобретаемых самими разработчиками, в API-интерфейсе Windows определены сотни кодов ошибок с по- мощью #define и HRESULT, а также множество вариаций простых булевских значений (bool, BOOL, VARIANT BOOL и т. д.). Более того, многие разработчики СОМ-приложений на языке С++ (а также VB 6) явно или неявно применяют небольшой набор стандартных СОМ-интерфейсов (наподобие ISupportErrorlnfo. IErrorlnfo или ICreateErrorlnfо) для возврата СОМ-клиенту понятной информации об ошибках. Очевидная проблема со всеми этими более старыми методика- ми – отсутствие симметрии. Каждая из них более-менее вписывает- ся в рамки какой-то одной технологии, одного языка и, пожалуй, даже одного проекта. В .NET поддерживается стандартная методика для генерации и выявления ошибок в исполняющей среде, называе- мая структурированной обработкой исключений (SEH – structured exception handling). Прелесть этой методики состоит в том, что она позволяет раз- работчикам использовать в области обработки ошибок унифициро- ванный подход, который является общим для всех языков, ориенти- рованных на платформу .NET. Благодаря этому, программист на C# может обрабатывать ошибки почти таким же с синтаксической точ- ки зрения образом, как и программист на VB и программист на С++, использующий C++/CLI. Дополнительное преимущество состоит в том, что синтаксис, который требуется применять для генерации и перехвата исключе- ний за пределами сборок и машин, тоже выглядит идентично. На- пример, при написании на C# службы Windows Communication Foundation (WCF) генерировать исключение SOAP для удаленного вызывающего кода можно с использованием тех же ключевых слов, которые применяются для генерации исключения внутри методов в одном и том же приложении. Еще одно преимущество механизма исключений .NET состоит в том, что в отличие от запутанных числовых значений, просто обо- значающих текущую проблему, они представляют собой объекты, в которых содержится читабельное описание проблемы, а также де- тальный снимок стека вызовов на момент, когда изначально воз- никло исключение. Более того, конечному пользователю можно предоставлять справочную ссылку, которая указывает на опреде- 16 ленный URL-адрес с описанием деталей ошибки, а также специаль- ные данные, определенные программистом. 2.3. Составляющие процесса обработки исключений в .NET Программирование со структурированной обработкой исклю- чений подразумевает использование четырех следующих связанных между собой сущностей: тип класса, который представляет детали исключения; член, способный генерировать (throw) в вызывающем коде экземпляр класса исключения при соответствующих обстоятельст- вах; блок кода на вызывающей стороне, ответственный за обра- щение к члену, в котором может произойти исключение; блок кода на вызывающей стороне, который будет обраба- тывать (или перехватывать (catch)) исключение в случае его воз- никновения. 2.4. Перехват исключений Принимая во внимание, что .NET Framework включает боль- шое количество предопределенных классов исключений, возникает вопрос: как их использовать в коде для перехвата ошибочных усло- вий? Для того чтобы справиться с возможными ошибочными си- туациями в коде C#, программа обычно делится на блоки трех раз- ных типов: Блоки try инкапсулируют код, формирующий часть нор- мальных действий программы, которые потенциально могут столк- нуться с серьезными ошибочными ситуациями. Блоки catch инкапсулируют код, который обрабатывает ошибочные ситуации, происходящие в коде блока try. Это также удобное место для протоколирования ошибок. Блоки finally инкапсулируют код, очищающий любые ре- сурсы или выполняющий другие действия, которые обычно нужно выполнить в конце блоков try или catch. Важно понимать, что этот блок выполняется независимо от того, сгенерированo исключение или нет. 1 Try и catch Основу обработки исключительных ситуаций в C# составляет пара ключевых слов try и catch. Эти ключевые слова действуют со- вместно и не могут быть использованы порознь. Ниже приведена 17 общая форма определения блоков try/catch для обработки исключи- тельных ситуаций: try { // Блок кода, проверяемый на наличие ошибок. } catch (ExcepType1 exOb) { // Обработчик исключения типа ExcepType1. } catch (ExcepType2 exOb) { // Обработчик исключения типа ExcepType2. } где ExcepType – это тип возникающей исключительной ситуации. Когда исключение генерируется оператором try, оно перехватыва- ется составляющим ему пару оператором catch, который затем об- рабатывает это исключение. В зависимости от типа исключения вы- полняется и соответствующий оператор catch. Так, если типы гене- рируемого исключения и того, что указывается в операторе catch, совпадают, то выполняется именно этот оператор, а все остальные пропускаются. Когда исключение перехватывается, переменная ис- ключения exOb получает свое значение. На самом деле указывать переменную exOb необязательно. Так, ее необязательно указывать, если обработчику исключений не требуется доступ к объекту ис- ключения, что бывает довольно часто. Для обработки исключения достаточно и его типа. Следует, однако, иметь в виду, что если исключение не гене- рируется, то блок оператора try завершается как обычно, и все его операторы catch пропускаются. Выполнение программы возобнов- ляется с первого оператора, следующего после завершающего опе- ратора catch. Таким образом, оператор catch выполняется лишь в том случае, если генерируется исключение. Давайте рассмотрим пример, в котором будем обрабатывать исключение, возникающее при делении числа на 0: using System; using System.Collections.Generic; using System.Linq; using System.Text; 18 namespace ConsoleApplication1 { class Program { static int MyDel(int x, int y) { return x / y; } static void Main() { try { Console.Write(«Введите x: «); int x = int.Parse(Console.ReadLine()); Console.Write(«Введите y: «); int y = int.Parse(Console.ReadLine()); int result = MyDel(x, y); Console.WriteLine(«Результат: « + result); } // Обрабатываем исключение, возникающее при деле- нии на ноль catch (DivideByZeroException) { Console.WriteLine(«Деление на 0 detected!!!\n»); Main(); } // Обрабатываем исключение при неккоректном вводе числа в консоль catch (FormatException) { Console.WriteLine(«Это НЕ число!!!\n»); Main(); } Console.ReadLine(); } 19 } } Данный простой пример наглядно иллюстрирует обработку исключительной ситуации при делении на 0 (DivideByZeroException), а также пользовательскую ошибку при вводе не числа (FormatException). 2 Последствия неперехвата исключений Перехват одного из стандартных исключений, как в приведен- ном выше примере, дает еще одно преимущество: он исключает аварийное завершение программы. Как только исключение будет сгенерировано, оно должно быть перехвачено каким-то фрагментом кода в определенном месте программы. Вообще говоря, если ис- ключение не перехватывается в программе, то оно будет перехваче- но исполняющей системой. Но дело в том, что исполняющая систе- ма выдаст сообщение об ошибке и прервет выполнение программы. Например, если убрать из предыдущего примера исключение FormatException, то при вводе неккоректной строки, IDE-среда VisualStudio выдаст предупреждающее сообщение: 20 Такие сообщения об ошибках полезны для отладки програм- мы, но, по меньшей мере, нежелательны при ее использовании на практике! Именно поэтому так важно организовать обработку ис- ключительных ситуаций в самой программе. Контрольные вопросы 1. Что такое исключительная ситуация? 2. Как можно обработать исключительную ситуацию? 21 3. Практическое занятие №3. «Анализ результатов тестирования» Целью работы является изучение способов анализа результа- тов тестирования ПО. Результатом практической работы является отчет, в котором должны быть приведены исходные коды програм- мы, тесты и их результаты. Для выполнения практической работы № 3 студент должен изучить приведенный ниже теоретический материал. Отчет сдается в распечатанном и электронном (файл Word) видах. Оценка выполненных тестов (Evaluation of the tests performed) Метрики покрытия/глубины тестирования (Coverage/ thoroughness measures) Критерии «адекватности» тестирования, в ряде случаев, требуют систематического выполнения тестов для оп- ределенных набора элементов программы, задаваемых ее архитек- турой или спецификацией. Соответствующие метрики позволяют оценить степень охвата характеристик системы (например, процент различных тестируемых параметров производительности) и глубину их детализации (например, случайное тестирование параметров производительности или с учетом граничных значений и т. п.). Та- кие метрики помогают прогнозировать вероятностное достижение заданных параметров качества системы. Введение искусственных дефектов (Fault seeding) «Своими ру- ками?! Никогда! ...» – такова, обычно, первая реакция на идею ис- кусственного внесения дефектов, например, в программный код. На практике, этот подход помогает классифицировать возможные ошибки и следующие за ними сбои, применяя в дальнейшем полу- ченные результаты для моделирования (пусть, часто, и интуитивно- го) возможных причин реальных сбоев, обнаруженных в процессе тестирования. Безусловно, данная техника должна использоваться с максимальной осторожностью опытными специалистами, хорошо представляющими общую архитектуру тестируемой программной системы и разбирающимися во еѐ внутренних связях. Оценка мутаций (Mutation score) Получаемое в процессе тес- тирования мутаций отношение «убитых» к общему числу сгенери- рованных мутантов помогает измерить эффективность выполняе- мых тестов. В силу специфики такой техники тестирования, количе- ственные оценки мутаций имеют практическое значение только для определенных типов систем. 22 Сравнение о относительная эффективность различных техник тестирования (Comparison and relative effectiveness of different techniques). Различные исследования в области тестирования связаны с по- пытками сравнения (с точки зрения достигаемого качества продук- та) разных подходов к тестированию. Когда мы говорим об «эффек- тивности» тестирования надо чѐтко договориться, то именно мы подразумеваем под эффективностью, желательно, в количественном выражении. Возможные варианты интерпретации этого понятия – число тестов (данной техники), необходимых для обнаружения пер- вого дефекта; отношение количества всех обнаруженных дефектов к дефектам, найденным с применением заданного подхода и т. п. Только обладая такого рода данными можно говорить о корректно- сти сравнения и оценки эффективности. Контрольные вопросы 1. Что такое метрики тестирования? 2. Как измеряется покрытие кода? 23 4. Лабораторная работа №1. Разработка тестового сценария Целью работы является изучение порядка разработки тестово- го сценария. Результатом работы является отчет, в котором должны быть приведены исходные коды программы, тестовый сценарий. Для выполнения работы студент должен изучить приведенный ниже теоретический материал. Отчет сдается в распечатанном и электронном (файл Word) видах. Создание тестовых сценариев (Test Case) в RequisitePro 1. Создать новый тип требований: конт. меню SpaceTravel → Properties → ф. Project Properties | вкл. Requirements Type | кн. Add → ф. Requirement Type | Name ← Scenario, Requirement Tag Prefix ← SC, остальные параметры – по умолчанию, кн. Ok → ф. Project Properties | кн. Ok 2. Создать новое требование: SpaceTravel → Supplementary Requirements → конт. меню → New → Requirement… → ф. Requirement Properties: pending | Type ← SC: Scenario, Name ← SC1 T1 (номер сценария использования + номер тестового сценария), кн. Ok 24 Проверить наличие нового требования в окне проекта. Аналогичным образом определить требования типа Scenario для остальных сценариев использования и тестовых сценариев. 3. Создать матрицу трассировки для связи требований Scenario с требованиями других типов: 25 3.1. Создать матрицу трассировки ф. Rational RequisitePro – SpaceTravel → конт. меню → New → View… → ф. View Properties | Name ← SC to UC, View Type ← Tra- ceability Matrix, Row Requirement Type ← SC: Scenario, Column Re- quirement Type ← UC: Use Case, кн. Ok → матрица трассировки на экране 3.2. Установить связи между требованиями типа SC и UC Сценарий использования (Use Case) и основанный на нем тес- товый сценарий (Scenario) → установить курсор на пересечении строки, соответствующей тестовому сценарию, и столбца, соответ- 26 ствующего сценарию использования → контекстное меню → Trace From. 3.3. Аналогичным образом установить связи для всех тестовых сценариев (один сценарий использования может привести к не- скольким тестовым сценариям) 3.4. Закрыть матрицу трассировки 4. Создать дерево трассировки ф. Rational RequisitePro – SpaceTravel → конт. меню → New → View… → ф. View Properties | Name ← Traceability Tree for SC, View Type ← Traceability Tree (Traced into), Row Requirement Type ← SC: Scenario, кн. Ok → результат на экране 27 Контрольные вопросы 1. Приведите порядок составления тестового сценария. 2. Что такое матрица трассировки? 3. Как можно задать матрицу трассировки? |