НЕГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ ЧАСТНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ «МОСКОВСКИЙ ФИНАНСОВОПРОМЫШЛЕННЫЙ УНИВЕРСИТЕТ “СИНЕРГИЯ” Факультет/Институт Направление/специальность подготовки: Информационные технологии (наименование факультета/ Института) 09.02.07 Информационные системы и программирование (код и наименование направления /специальности подготовки) очная Форма обучения: (очная, очно-заочная, заочная) Отчет по лабораторной работе 3 на тему Лабораторный практикум 3 «Разработка модульных тестов и достижение 100% покрытия кода» (наименование темы) по дисциплине Проектирование и дизайн информационных систем (наименование дисциплины) Обучающийся Группа Преподаватель Бондарь Николай (ФИО) ДКИП 312 Скопин Владимир Игоревич (ФИО) Москва 2025 (подпись) (подпись) Лабораторная работа №3 Тема: «Разработка модульных тестов и достижение 100% покрытия кода» Цель работы Освоить принципы модульного тестирования, применение фреймворков тестирования (например, xUnit, NUnit, MSTest), а также научиться анализировать и повышать покрытие кода тестами до 100%. 1. Выбор проекта для тестирования: Для тестирования был выбран проект «Менеджер задач» — работа со списком задач: (CRUD-операции), фильтрации, поиск, исключения при некорректных данных. Его консольный вариант, реализованный на языке C# .NET 8 со следующей структурой: Рис 1. Структура и классы проекта Рис 2. Структура и классы проекта Тестирования В настоящей работе реализована классическая структура — отдельный проект для приложения и отдельный для тестов. Методы включают условия if/else, switch или циклы; обработку исключений; 2. Модульные тесты: Для модульного тестирования были разработаны и использовались тесты на основе xUnit. Полные тексты Проекта и Проект с тестами прилагаются к отчету 3. Покрытие кода Для исследования покрытия кода использовался инструмент coverlet и или встроенные средства IDE. Скриншоты работы с покрытием кода. Рис 3. Запуск тестов на покрытие кода. Рис 4 Результаты проведенного теста на покрытие кода 4. Полный запуск всех тестов Рис.5 Зпуск тестирования Рис 6. Результаты тестирования и поиск ошибочного места в программе. После проведения автоматического тестирования осуществляется экспертный анализ «плохих» тестов. Общий алгоритм приведен на рисунке 6. Действия 1,2,3 приводят к проявлению в правой половине окна cтека, вызвавшего ошибку и шаг 4 приводит (при двойном нажатии к месту в программе, которое ее вызвало (шаг 5 ). Здесь необходимо принять решение, что делать с кодом. Рис 7. Результаты тестирования после исправления ошибочного кода Вопросы 1. Чем отличаются модульные, интеграционные и системные тесты? Модульные тесты (Unit Tests) Что тестируют: Отдельные "единицы" - классы, методы Изоляция: Полная. Все зависимости заменяются "муляжами" Скорость: Очень быстрые (миллисекунды) Пример: Проверка, что метод AddTask() правильно создает задачу Интеграционные тесты (Integration Tests) Что тестируют: Взаимодействие нескольких компонентов Изоляция: Частичная. Используются реальные БД, файлы, API Скорость: Средние (секунды) Пример: Проверка, что TaskManager корректно сохраняет задачи в базу данных Системные тесты (System Tests / E2E) Что тестируют: Всю систему целиком Изоляция: Минимальная. Все реальные компоненты Скорость: Медленные (минуты/часы) Пример: Полный сценарий: пользователь открывает приложение → создает задачу → отмечает выполненной → удаляет Ключевые отличия: Масштаб: Метод → Компоненты → Вся система Изоляция: Полная → Частичная → Практически нет Скорость: Быстро → Средне → Медленно Цель: Корректность логики → Корректность взаимодействия → Работоспособность системы Пирамида тестирования (идеальное соотношение): 70% - Модульные (быстрые, надежные) 20% - Интеграционные (проверяют интеграции) 10% - Системные (проверяют полные сценарии) Простая аналогия: Модульные = Проверка двигателя отдельно от машины Интеграционные = Проверка как двигатель работает с коробкой передач Системные = Тест-драйв всей готовой машины 2. Почему невозможно гарантировать качество только на основе 100% покрытия? 100% покрытие говорит: "Мы вызвали каждую строку кода". Но не говорит: "Мы проверили все возможные сценарии работы". Например: Тест вызывает метод Calculate(2, 2) → покрытие 100% Но не тестирует Calculate(0, 0), Calculate(-1, 1000), Calculate(null, null) 3. Какие типы покрытия существуют (по строкам, по ветвям, по условиям) Выделяют следующие типы покрытия кода 1. Покрытие по строкам (Line Coverage) Что: Сколько строк кода было выполнено Простота: Легко достичь, но недостаточно 2. Покрытие по ветвям (Branch Coverage) Что: Все ветви if/else, switch протестированы Строже: Требует тестировать ВСЕ варианты выполнения Пример: if-else → нужно протестировать обе ветки 3. Покрытие по условиям (Condition Coverage) Что: Все комбинации логических условий проверены Самый строгий: if (A && B) → нужно 4 теста для всех комбинаций A и B Практически: Часто недостижимо для сложных условий 4. Покрытие по путям (Path Coverage) Что: Все возможные пути выполнения программы Теоретически идеально, но экспоненциально сложно Редко используется на практике из-за сложности Иерархия от слабого к строгому: Строки → Ветви → Условия → Пути Чем правее, тем: надежнее качество тестов, НО сложнее достичь и дороже в поддержке 4. Какую роль играют mock-объекты? Mock-объекты это: искусственные объекты, которые имитируют поведение реальных зависимостей в тестах. Основные роли: 1.Изоляция тестов - Тестируем ОДИН компонент, не зависимо от других, Проблемы - в базе данных/API не ломают unit-тесты 2.Контроль зависимостей - Задаём точное поведение: "верни эти данные", "вызовись 3 раза" Создаём нужные сценарии - : успех, ошибка, исключение 3. Ускорение тестов Не ходим в реальную БД (медленно).Не зависим от - сети/внешних сервисов 4. Тестирование труднодоступных сценариев - "А что если сервер упадет?", "А если БД вернёт 10 миллионов записей?","А если файловая система переполнена?" 5. Проверка взаимодействия - "Метод А вызвал метод Б с правильными параметрами?","Сколько раз вызвался внешний сервис?" 4. Что делать, если добиться 100% покрытия невозможно? Сфокусироваться на важном : бизнес-логика должна обладать максимальным покрытием. Можно писать вспомогательный код. Установить реалистичные цели: 80-90% по строкам - хороший уровень 70-80% по ветвям - достаточная надежность Использовать исключения