Tdd Workbook

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

0 comments

Post a comment

    Post a comment
    Embed Video
    Edit your comment Cancel

    Favorites, Groups & Events

    Tdd Workbook - Presentation Transcript

    1. Учебный Центр Luxoft www.luxoft.ru/edu Разработка через тестирование (Test Driven Development) Development) EKrivosheev@luxoft.com О вашем инструкторе Имя Статусы Контакты 1-2 Рабочие материалы Презентация Рабочая среда на ПК 1-3 1
    2. Учебный Центр Luxoft www.luxoft.ru/edu Цели курса... курса... По окончании данного курса слушатели: получат общее понятие о целях и задачах тестирования освоят технологии модульного тестирования научатся применять шаблоны при разработке модульных тестов 1-4 Цели курса… курса… По окончании данного курса слушатели: ознакомятся с синтаксисом и приобретут опыт практического использования одной из сред тестирования семейства xUnit (JUnit/NUnit) освоят практику разработки через тестирование 1-5 ...Цели курса ...Цели По окончании данного курса слушатели: научаться применять шаблоны TDD получат практический опыт разработки приложения с использованием TDD 1-6 2
    3. Учебный Центр Luxoft www.luxoft.ru/edu Необходимая подготовка Слушатели должны: иметь опыт разработки на одном из языков программирования: Java/C# 1-7 Знакомство Напишите свое имя на пирамидке, пожалуйста Ваш опыт разработки на Java/С# Ваш опыт работы с JUnit Ваш опыт разработки по TDD 1-8 Расписание День 1 Модуль 0: Введение Модуль 1: Тестирование в разработке ПО Модуль 2: Библиотека модульного тестирования JUnit День 2 Модуль 3: Test-driven development Модуль 4: TDD Workshop 1-9 3
    4. Учебный Центр Luxoft www.luxoft.ru/edu Общие рекомендации Пожалуйста, Отключите телефоны Задавая вопросы, старайтесь придерживаться темы обсуждения Если у вас возникли вопросы, не относящиеся к теме, пометьте их, и инструктор будет рад ответить при подходящем случае 1-10 Организация обучения Время начала и конца занятий Перерывы Питание 1-11 Вопросы и ответы 1-12 4
    5. Учебный Центр Luxoft www.luxoft.ru/edu План курса Модуль 1: Модульное тестирование Тестирование как способ обеспечения качества продукта Уровни тестирования Цели и задачи модульного тестирования Покрытие кода Унаследованный код Организационные аспекты тестирования 2-13 Тестирование – способ обеспечения качества продукта Качество ПО Качество программного продукта характеризуется набором свойств, определяющих, насколько продукт "хорош" с точки зрения заинтересованных сторон 2-14 Тестирование – способ обеспечения качества продукта Качество ПО Заинтересованными сторонами являются: заказчик продукта спонсор конечный пользователь разработчики тестировщики продукта инженеры поддержки сотрудники отделов маркетинга, обучения и продаж 2-15 5
    6. Учебный Центр Luxoft www.luxoft.ru/edu Тестирование – способ обеспечения качества продукта Качество ПО Каждый из участников может иметь различное представление о продукте и о том, насколько он хорош или плох (то есть о том, насколько высоко качество продукта) 2-16 Тестирование – способ обеспечения качества продукта Качество ПО Таким образом, постановка задачи обеспечения качества продукта выливается в задачи: определения заинтересованных лиц их критериев качества нахождения оптимального решения, удовлетворяющего этим критериям 2-17 Тестирование – способ обеспечения качества продукта Тестирование: общие понятия Тестирование является одним из наиболее устоявшихся способов обеспечения качества разработки программного обеспечения Оно является одним из эффективных средств современной системы обеспечения качества программного продукта Верификация и валидация ПО 2-18 6
    7. Учебный Центр Luxoft www.luxoft.ru/edu Тестирование – способ обеспечения качества продукта Тестирование: общие понятия С технической точки зрения, тестирование заключается в: выполнении приложения на некотором множестве исходных данных сверке получаемых результатов с заранее известными (эталонными) с целью установить соответствие различных свойств и характеристик приложения заказанным свойствам 2-19 Тестирование – способ обеспечения качества продукта Тестирование: общие понятия Тестирование является одной из основных фаз разработки программного продукта (наряду с Дизайном приложения и Разработкой кода) Оно характеризуется достаточно большим вкладом в суммарную трудоемкость разработки продукта 2-20 Тестирование – способ обеспечения качества продукта Эффективность автоматизации Широко известна оценка распределения трудоемкости между фазами создания программного продукта: 40%-20%-40%* (см. рисунок) Оценка распределения трудоемкости и стоимости исправления ошибки *Котляров В.П., Основы тестирования программного обеспечения 2-21 7
    8. Учебный Центр Luxoft www.luxoft.ru/edu Тестирование – способ обеспечения качества продукта Эффективность автоматизации Следовательно, наибольший эффект в снижении трудоемкости может быть получен прежде всего на фазах Design и Testing А значит и основные вложения в автоматизацию или генерацию кода следует осуществлять, прежде всего, на этих фазах 2-22 Тестирование – способ обеспечения качества продукта Эффективность автоматизации Как видно из графика, стоимость исправления ошибок минимальна на стадиях дизайна и разработки Т.е. было бы неплохо обнаруживать большую часть ошибок до начала фазы тестирования 2-23 Тестирование – способ обеспечения качества продукта Эффективность автоматизации Однако технологии автоматизированного тестирования дизайна (верификации требований и спецификаций) только начинают появляться Трейсинг (дизайна и требований) В то же время, автоматизированное тестирование кода является широко распространенной практикой 2-24 8
    9. Учебный Центр Luxoft www.luxoft.ru/edu Тестирование – способ обеспечения качества продукта Эффективность автоматизации Таким образом, процесс тестирования затрагивает все фазы производства ПО, однако не на всех фазах он может быть успешно автоматизирован 2-25 Тестирование – способ обеспечения качества продукта Уровни тестирования Разработка системы, как правило, идет на различных уровнях: вначале разрабатывается концепция системы, системные требования затем архитектура системы, ее разбиение на модули затем разрабатываются отдельные модули Процесс верификации также разбивается на отдельные уровни 2-26 План курса Модуль 1: Модульные тесты Тестирование как способ обеспечения качества продукта Уровни тестирования Цели и задачи модульного тестирования Покрытие кода Унаследованный код Организационные аспекты тестирования 2-27 9
    10. Учебный Центр Luxoft www.luxoft.ru/edu Уровни тестирования Уровни тестирования системное тестирование, в ходе которого тестируется система в целом; интеграционное тестирование, в ходе которого тестируются группы взаимодействующих модулей и компонент системы; модульное тестирование, в ходе которого тестируются отдельные компоненты 2-28 Уровни тестирования Системное тестирование Системное тестирование (System Testing): Основной задачей системного тестирования является проверка как функциональных, так и нефункциональных требований в системе в целом 2-29 Уровни тестирования Системное тестирование В ходе системного тестирования выявляются следующие дефекты: неверное использование ресурсов системы непредусмотренные комбинации данных пользовательского уровня несовместимость с окружением непредусмотренные сценарии использования отсутствующая или неверная функциональность неудобство использования и т.д. 2-30 10
    11. Учебный Центр Luxoft www.luxoft.ru/edu Уровни тестирования Системное тестирование Для минимизации рисков, связанных с особенностями поведения в системы в той или иной среде, во время тестирования рекомендуется использовать окружение максимально приближенное к тому, на которое будет установлен продукт после выдачи 2-31 Уровни тестирования Интеграционное тестирование Интеграционное тестирование (Integration Testing): Интеграционное тестирование предназначено для проверки связи между компонентами, а также взаимодействия с различными частями системы (операционной системой, оборудованием либо связи между различными системами) 2-32 Уровни тестирования Интеграционное тестирование Интеграционное тестирование так же может проводиться на различных уровнях: Компонентный: проверяется взаимодействие между компонентами системы после проведения компонентного (модульного) тестирования Системный: проверяется взаимодействие между разными системами после проведения системного тестирования 2-33 11
    12. Учебный Центр Luxoft www.luxoft.ru/edu Уровни тестирования Модульное тестирование Компонентное или Модульное тестирование (Component or Unit Testing): Модульное тестирование проверяет функциональность и ищет дефекты в частях приложения, которые доступны и могут быть протестированы по отдельности (модули программ, объекты, классы, функции и т.д.) 2-34 Уровни тестирования Модульное тестирование Обычно компонентное (модульное) тестирование проводится вызывая код, который необходимо проверить (при поддержке среды разработки) Все найденные дефекты, как правило, исправляются в коде без формального их описания в системе управления ошибками 2-35 План курса Модуль 1: Модульные тесты Тестирование как способ обеспечения качества продукта Уровни тестирования Цели и задачи модульного тестирования Покрытие кода Унаследованный код Организационные аспекты тестирования 2-36 12
    13. Учебный Центр Luxoft www.luxoft.ru/edu Цели и задачи модульного тестирования Модульное тестирование Каждая сложная программная система состоит из отдельных частей - модулей, выполняющих ту или иную функцию в составе системы Для того, чтобы удостовериться в корректной работе всей системы, необходимо вначале протестировать каждый модуль системы по отдельности 2-37 Цели и задачи модульного тестирования Модульное тестирование В случае возникновения проблем при тестировании системы в целом это позволяет проще выявить модули, вызвавшие проблему, и устранить соответствующие дефекты в них Такое тестирование модулей по отдельности получило называние модульного тестирования (unit testing) 2-38 Цели и задачи модульного тестирования Определения Тестовый драйвер (среда, фреймворк) – система, позволяющая выполнять и контролировать результат выполнения тестов. Как правило, позволяет также корректировать входные данные, включает в себя систему обработки исключительных ситуаций и восстановления, средства параметризации тестов и управления данными 2-39 13
    14. Учебный Центр Luxoft www.luxoft.ru/edu Цели и задачи модульного тестирования Определения Заглушка – объект, предназначенный для симуляции поведения реального объекта во время тестирования Тест-план – представляет собой документ, в котором перечислены либо все тестовые примеры, необходимые для тестирования системы, либо часть тестовых примеров, объединенных по определенному признаку 2-40 Цели и задачи модульного тестирования Определения Тест-требования – содержат описание требований по проверке всех основных функций системы Для каждого модуля, подвергаемого тестированию, разрабатывается тестовое окружение, включающее в себя драйвер и заглушки, готовятся тест-требования и тест-планы, описывающие конкретные тестовые примеры 2-41 Цели и задачи модульного тестирования Цели модульного тестирования Основная цель модульного тестирования - удостовериться в соответствии требованиям каждого отдельного модуля системы перед тем, как будет произведена его интеграция в состав системы 2-42 14
    15. Учебный Центр Luxoft www.luxoft.ru/edu Цели и задачи модульного тестирования Задачи модульного тестирования В ходе модульного тестирования решаются следующие основные задачи: Поиск и документирование несоответствий требованиям Поддержка разработки и рефакторинга низкоуровневой архитектуры системы и межмодульного взаимодействия Поддержка рефакторинга модулей Поддержка устранения дефектов и отладки2-43 Цели и задачи модульного тестирования Задачи модульного тестирования Рефакторинг – процесс полного или частичного преобразования внутренней структуры программы при сохранении её внешнего поведения. В его основе лежит последовательность небольших эквивалентных (т.е., сохраняющих поведение) преобразований. 2-44 Цели и задачи модульного тестирования Задачи модульного тестирования Поиск и документирование несоответствий требованиям - классическая задача тестирования, включающая в себя не только разработку тестового окружения и тестовых примеров, но и выполнение тестов, протоколирование результатов выполнения, составление отчетов о проблемах 2-45 15
    16. Учебный Центр Luxoft www.luxoft.ru/edu Цели и задачи модульного тестирования Задачи модульного тестирования Поддержка разработки и рефакторинга низкоуровневой архитектуры системы и межмодульного взаимодействия – модульные тесты помогают выявить проблемы в дизайне системы и нелогичные или запутанные механизмы работы с модулем 2-46 Цели и задачи модульного тестирования Задачи модульного тестирования Поддержка устранения дефектов и отладки сопряжена с обратной связью, которую получают разработчики от тестировщиков в виде отчетов о проблемах 2-47 Цели и задачи модульного тестирования Задачи модульного тестирования Подробные отчеты о проблемах, составленные на этапе модульного тестирования, позволяют локализовать и устранить многие дефекты в программной системе на ранних стадиях ее разработки или разработки ее новой функциональности 2-48 16
    17. Учебный Центр Luxoft www.luxoft.ru/edu Определение модуля Проблемы определения модуля В силу того, что модули, подвергаемые тестированию, обычно невелики по размеру, модульное тестирование считается наиболее простым (хотя и достаточно трудоемким) этапом тестирования системы Однако, несмотря на внешнюю простоту, с модульным тестированием сопряжены две проблемы: 2-49 Определение модуля Проблемы определения модуля не существует единых принципов определения того, что в точности является отдельным модулем трактовка понятия модульного тестирования - понимается ли под ним обособленное тестирование модуля, работа которого поддерживается только тестовым окружением, или речь идет о проверке корректности работы модуля в составе уже разработанной системы 2-50 Определение модуля Традиционное определение Традиционное определение модуля с точки зрения его тестирования: модуль - это компонент минимального размера, который может быть независимо протестирован в ходе верификации программной системы В реальности часто возникают проблемы с тем, что считать модулем 2-51 17
    18. Учебный Центр Luxoft www.luxoft.ru/edu Определение модуля Альтернативные определения Существует несколько подходов к данному вопросу: модуль - это часть программного кода, выполняющая одну функцию с точки зрения функциональных требований модуль - это программный модуль, т.е. минимальный компилируемый элемент программной системы 2-52 Определение модуля Альтернативные определения модуль - это задача в списке задач проекта (с точки зрения его менеджера) модуль - это участок кода, который может уместиться на одном экране или одном листе бумаги модуль - это один класс или их множество с единым интерфейсом модуль - это одна функция или метод 2-53 Определение модуля Границы модуля Обычно за тестируемый модуль принимается либо программный модуль (единица компиляции) в случае, если система разрабатывается на процедурном языке, или класс, если система разрабатывается на объектно- ориентированном языке 2-54 18
    19. Учебный Центр Luxoft www.luxoft.ru/edu Определение модуля Границы модуля В случае систем, написанных на процедурных языках, процесс тестирования модуля происходит достаточно просто – для каждого модуля разрабатывается: тестовый драйвер, вызывающий функции модуля и собирающий результаты их работы набор заглушек, которые имитируют поведение функций, содержащихся в других модулях 2-55 Определение модуля Границы модуля При тестировании объектно- ориентированных систем существует ряд особенностей, прежде всего вызванных инкапсуляцией данных и методов в классах (декомпозиция класса нарушит принцип инкапсуляции, согласно которому объекты каждого класса должны вести себя как единое целое с точки зрения других объектов) 2-56 Определение модуля Границы модуля Кроме того, более мелкое деление классов и использование отдельных методов в качестве тестируемых модулей нецелесообразно, поскольку для тестирования каждого метода потребуется разработка тестового окружения, сравнимого по сложности с уже написанным программным кодом класса 2-57 19
    20. Учебный Центр Luxoft www.luxoft.ru/edu Определение модуля Компонентное тестирование Процесс тестирования классов как модулей иногда называют компонентным тестированием В ходе такого тестирования проверяется взаимодействие методов внутри класса и правильность доступа методов к внутренним данным класса 2-58 Определение модуля Компонентное тестирование На данном этапе возможно обнаружение не только стандартных дефектов (связанных с выходами за границы диапазона или неверно реализованными требованиями), но и специфических дефектов объектно-ориентированного программного обеспечения 2-59 Определение модуля Специфические дефекты ООП Такими дефектами являются: дефекты инкапсуляции, в результате которых, например, сокрытые данные класса оказываются недоступными для соответствующих публичных методов дефекты наследования, при наличии которых схема наследования блокирует важные данные или методы от классов- потомков 2-60 20
    21. Учебный Центр Luxoft www.luxoft.ru/edu Определение модуля Специфические дефекты ООП дефекты полиморфизма, при которых полиморфное поведение класса оказывается распространенным не на все возможные классы дефекты инстанцирования, при которых во вновь создаваемых объектах класса не устанавливаются корректные значения по умолчанию параметров и внутренних данных класса 2-61 Определение модуля Проблемы тестирования в ООП Однако, выбор класса в качестве тестируемого модуля имеет и ряд сопряженных проблем: Определение степени полноты тестирования класса Протоколирование состояний объектов и их изменений Тестирование изменений 2-62 Определение модуля Проблемы тестирования в ООП Определение степени полноты тестирования класса: В том случае, если в качестве тестируемого модуля выбран класс, не совсем ясно, как определять степень полноты его тестирования Классический критерий полноты покрытия: тесты можно считать полными, если выполнены все структурные элементы всех методов, как публичных, так и скрытых 2-63 21
    22. Учебный Центр Luxoft www.luxoft.ru/edu Определение модуля Проблемы тестирования в ООП Однако существует альтернативный подход к тестированию класса: все публичные методы должны предоставлять пользователю данного класса согласованную схему работы В этом случае достаточно проверить типичные корректные и некорректные сценарии работы с данным классом 2-64 Определение модуля Проблемы тестирования в ООП Протоколирование состояний объектов и их изменений: Некоторые методы класса предназначены не для выдачи информации пользователю, а для изменения внутренних данных объекта класса 2-65 Определение модуля Проблемы тестирования в ООП Значение внутренних данных объекта определяет его состояние в каждый определенный момент времени, а вызов методов, изменяющих данные, изменяет и состояние объекта При тестировании классов необходимо проверять, что класс адекватно реагирует на внешние вызовы в любом из состояний 2-66 22
    23. Учебный Центр Luxoft www.luxoft.ru/edu Определение модуля Проблемы тестирования в ООП Однако, зачастую из-за инкапсуляции данных невозможно определить внутреннее состояние класса программными способами внутри драйвера Автоматизированное тестирование в этом случае может лишь определить, по всем ли выявленным состояниям осуществлялись переходы и все ли возможные реакции проверялись 2-67 Определение модуля Проблемы тестирования в ООП Тестирование изменений: В результате рефакторинга только одного класса, как правило, не меняется его внешний интерфейс с другими классами (интерфейсы меняются при рефакторинге сразу нескольких классов) 2-68 Определение модуля Проблемы тестирования в ООП В результате обычных эволюционных изменений системы у класса может меняться внешний интерфейс: по формальным признакам –изменяются имена и состав методов, их параметры по функциональным признакам – при сохранении внешнего интерфейса меняется логика работы методов (contract) 2-69 23
    24. Учебный Центр Luxoft www.luxoft.ru/edu Определение модуля Проблемы тестирования в ООП Для проведения модульного тестирования класса после таких изменений потребуется изменение драйвера и, возможно, заглушек Но только модульного тестирования в данном случае недостаточно, необходимо также проводить и интеграционное тестирование данного класса вместе со всеми классами, которые связаны с ним по данным или по управлению 2-70 Определение модуля Проблемы тестирования в ООП Связь по управлению реализуется путем вызова одного модуля из другого. Вызванный модуль после завершения своей работы возвращает управление вызвавшему его модулю. Связь по данным реализуется двумя способами: использование параметров при вызове модулей использование общих областей данных 2-71 Отличия модульного тестирования и отладки Определение отладки Отладка — этап разработки компьютерной программы, на котором обнаруживают, локализуют и устраняют ошибки Чтобы понять, где возникла ошибка, приходится: узнавать текущие значения переменных выяснять, по какому пути выполнялась программа 2-72 24
    25. Учебный Центр Luxoft www.luxoft.ru/edu Отличия модульного тестирования и отладки Технологии отладки Существуют две взаимодополняющие технологии отладки: Использование отладчиков — программ, которые включают в себя пользовательский интерфейс для пошагового выполнения программы, с остановками на указанных строках исходного кода или при выполнении определённого условия 2-73 Отличия модульного тестирования и отладки Технологии отладки Вывод текущего состояния программы с помощью расположенных в критических точках программы операторов вывода — на экран, принтер, или в файл (вывод отладочных сведений в файл называется журналированием) 2-74 Отличия модульного тестирования и отладки Проблемы при отладке Однако использование отладки сопряжено с рядом проблем: Отладка метода, глубоко «закопанного» в большом приложении, или воспроизведение тестовой ситуации зачастую становятся чрезвычайно трудоемкими 2-75 25
    26. Учебный Центр Luxoft www.luxoft.ru/edu Отличия модульного тестирования и отладки Проблемы при отладке Иногда не удается проверить только что написанный код, потому что он еще нигде и никак не используется В некоторых ситуациях программисты даже разрабатывают специальные утилиты, позволяющие отладить тот или иной компонент отдельно от всей системы 2-76 Отличия модульного тестирования и отладки Преимущества модульных тестов При использовании модульных тестов подобных проблем просто не возникает: Если нужно что-нибудь проверить – пишется соответствующий тест Тесты представляют собой практически идеальную отладочную среду: они находятся полностью под контролем программиста и позволяют вызвать любой код в широком диапазоне условий 2-77 Отличия модульного тестирования и отладки Преимущества модульных тестов Для большинства ошибок, найденных при модульном тестировании, отладка вообще не требуется, поскольку точно известны: место их возникновения (код, который был написан только что) условия воспроизведения (тест, который сейчас отлаживается) 2-78 26
    27. Учебный Центр Luxoft www.luxoft.ru/edu Организация модульного тестирования Фазы тестирования Формально, процесс тестирования можно разделить на следующие фазы: планирование разработка набора тестов выполнение тестов и сбор статистики, каждая из которых характеризуется определенным набором активностей 2-79 Организация модульного тестирования Фаза планирования На этапе планирования формируются общие принципы тестирования в проекте: степень полноты и охвата тестирования источники входных и выходных данных технологии проверки результатов и формат их записи требования к завершению тестирования 2-80 Организация модульного тестирования Фаза планирования А так же анализируются свойства каждого из модулей: функциональные требования дополнительные требования (напр. системные) характеристики входных и выходных данных определение состояний модуля (если модуль можно представить в виде конечного автомата) 2-81 27
    28. Учебный Центр Luxoft www.luxoft.ru/edu Организация модульного тестирования Фаза планирования После анализа требований к модулям, возможно возникнет необходимость внести корректировки в общие принципы тестирования После этого фазу планирования можно считать оконченной 2-82 Организация модульного тестирования Фаза разработки тестов В ходе этапа разработки тестов должны быть решены следующие задачи: разработка архитектуры тестовых наборов разработка тестовых сценариев (test- case) и тестовых наборов (test-suite) разработка нефункциональных тестов, (напр., основанных на архитектуре) составление спецификаций тестов (документирование) 2-83 Организация модульного тестирования Фаза разработки тестов тестовый сценарий – определение набора входных данных теста, условий выполнения и ожидаемых результатов, указанных с целью оценки некоторого аспекта тестируемого элемента тестовый набор – набор тестовых сценариев, объединенных по какому-либо признаку (напр., тестирующих конкретный модуль или его часть его функциональности) 2-84 28
    29. Учебный Центр Luxoft www.luxoft.ru/edu Организация модульного тестирования Фаза разработки тестов В ходе разработки тестовых сценариев так же выполняются следующие задачи: формируются тестовые наборы данных создается тестовое окружение осуществляется интеграция тестового окружения с тестируемым модулем 2-85 Организация модульного тестирования Фаза выполнения и анализа После того, как все тесты реализованы, они выполняются в ручном или автоматическом режиме Вне зависимости от вида тестирования в ходе этого этапа решаются две задачи: выполнение тестовых примеров сбор и анализ результатов тестирования 2-86 Организация модульного тестирования Фаза выполнения и анализа Сбору подлежит следующая информация: результат выполнения каждого тестового сценария (прошел/не прошел) информация об информационном окружении системы в случае, если тест не прошел информация о ресурсах, которые потребовались для выполнения тестового примера 2-87 29
    30. Учебный Центр Luxoft www.luxoft.ru/edu Организация модульного тестирования Фаза выполнения и анализа По результатам анализа этой информации производится изменение требований, программного кода, тестов или тестового окружения Этапы разработки (доработки), реализации и выполнения тестов продолжаются до тех пор, пока не будет достигнут критерий завершения модульного тестирования (напр., 90% покрытие тестами исходного кода) 2-88 Использование mock- и mock- stub-объектов stub- Общие понятия Классы (модули), как правило, редко бывают полностью изолированными, и используют в своей работе другие классы Например, слой бизнес логики (Business Logic Layer) часто работает с другими объектами бизнес логики или обращается к слою доступа к данным (Data Access Layer) 2-89 Использование mock- и mock- stub-объектов stub- Общие понятия В таких случаях на помощь приходят mock-объекты (заглушки), предназначенные для симуляции поведения реальных объектов во время тестирования Понятие mock-объект может обозначать как любой из видов заглушек (Test Doublers, тестовых дублеров), так и конкретный их вид – mock-объекты 2-90 30
    31. Учебный Центр Luxoft www.luxoft.ru/edu Использование mock- и mock- stub-объектов stub- Общие понятия Все тест-дублеры делятся на 4 группы*: dummy-объекты fake-объекты stub-объекты mock-объекты *Gerard Meszaros – “XUnit Test Patterns”, Martin Fawler – “Refactoring: Improving the Design of Existing Code” 2-91 Использование mock- и mock- stub-объектов stub- Dummy-объекты Dummy – пустые объекты, которые передаются в вызываемые внутренние методы, но не используются (предназначены лишь для заполнения параметров методов) 2-92 Использование mock- и mock- stub-объектов stub- Dummy-объекты public void testInvoice_addLineItem() { final int QUANTITY = 1; Product product = new Product(getUniqueNumberAsString(), getUniqueNumber()); City city = new City(“Vladivostok", “Russia”); Address address = new Address(“Lenin St, 12", city, “650243"); Customer customer= new Customer(getUniqueNumberAsString(), getUniqueNumberAsString(), address); Invoice inv = new Invoice(customer); // Вызов inv.addItemQuantity(product, QUANTITY); // Проверка List lineItems = inv.getLineItems(); assertEquals("number of items", lineItems.size(), 1); LineItem actual = (LineItem)lineItems.get(0); LineItem expItem = new LineItem(product, QUANTITY); assertLineItemsEqual("",expItem, actual); } 2-93 31
    32. Учебный Центр Luxoft www.luxoft.ru/edu Использование mock- и mock- stub-объектов stub- Dummy-объекты public void testInvoice_addLineItem() { final int QUANTITY = 1; Product product = new Product(“Dummy product name”, getUniqueNumber()); Invoice inv = new Invoice(new DummyCustomer); // Вызов inv.addItemQuantity(product, QUANTITY); // Проверка List lineItems = inv.getLineItems(); assertEquals("number of items", lineItems.size(), 1); LineItem actual = (LineItem)lineItems.get(0); LineItem expItem = new LineItem(product, QUANTITY); assertLineItemsEqual("",expItem, actual); } 2-94 Использование mock- и mock- stub-объектов stub- Dummy-объекты public class DummyCustomer implements ICustomer { public DummyCustomer() { // Конструктор оставляем пустым – никакой инициализации не требуется } public int getTimeZone() { throw new RuntimeException(“Этот метод не должен быть вызван!”); } } 2-95 Использование mock- и mock- stub-объектов stub - Stub-объекты Stub: объекты, которые предоставляют заранее заготовленные ответы на вызовы во время выполнения теста и обычно не отвечающие ни на какие другие вызовы, которые не требуются в тесте также могут запоминать какую-то дополнительную информацию о количестве вызовов, параметрах и возвращать их потом тесту для проверки 2-96 32
    33. Учебный Центр Luxoft www.luxoft.ru/edu Использование mock- и mock- stub-объектов stub - Stub-объекты Выделяют несколько видов stub’ов, в зависимости от целей применения: Responder (ответчик) используется для эмуляции корректного поведения объекта как правило, используется в happy-path- тестах используется, когда реальный объект еще не реализован, либо недоступен в девелоперском окружении 2-97 Использование mock- и mock- stub-объектов stub - Stub-объекты Saboteur (диверсант) используется для эмуляции некорректного поведения объекта призван всеми возможными способами вызвать «крах» системы, выдавая некорректные значения, исключения и т.п. вне зависимости от получаемых входных данных используется для тестирования обработки различных отказов 2-98 Использование mock- и mock- stub-объектов stub - Stub-объекты Temporary stub (временная заглушка) используется для замены еще не реализованного объекта как правило, возвращает hardcoded- значения заменяется реальным объектом, как только это становится возможным, либо сам постепенно становится этим объектам, постепенно получая функциональное наполнение 2-99 33
    34. Учебный Центр Luxoft www.luxoft.ru/edu Использование mock- и mock- stub-объектов stub - Stub-объекты Entity chain snipping используется для замены сложной системы взаимодействующих объектов одним упрощает процедуру инициализации тестового окружения делает тесты более понятными 2-100 Использование mock- и mock- stub-объектов stub - Stub-объекты В зависимости от реализации, stub’ы бывают: Hard-coded stub (статические) на любые запросы выдают одно и то же жестко прописанное значение как правило, пишутся для конкретного теста, либо их небольшого набора 2-101 Использование mock- и mock- stub-объектов stub - Stub-объекты Configurable stub (конфигурируемые) используются для того, чтобы для каждого теста не писать свой статический stub тест конфигурирует stub во время своей инициализации 2-102 34
    35. Учебный Центр Luxoft www.luxoft.ru/edu Использование mock- и mock- stub-объектов stub - Fake-объекты К сожалению, редко можно обойтись простыми dummy- и stub-объектами Иногда, тестируемый метод может обращаться к базе данных, web-сервисам, или файловой системе В этом случае, наш объект должен уметь «симулировать» требуемые действия, выполняя более простой код (напр., сохранять данные в памяти вместо БД) 2-103 Использование mock- и mock- stub-объектов stub - Fake-объекты Fake – объекты, имеющие работающие реализации, но в таком виде, который делает их неподходящими для production- кода Примеры fake-объектов: Fake database – реальная БД заменяется аналогичным по функциональности, но более «легким» аналогом (который даст выигрыш при тестировании, но не выдержит production нагрузки) 2-104 Использование mock- и mock- stub-объектов stub - Fake-объекты In-memory database – реальная БД заменяется набором HashTable’s, либо её небольшим «слепком», размещаемым в памяти на время выполнения Fake web-service – реальный web-сервис заменяется локальной реализацией, возвращающей определенное значение (или набор значений), что позволяет выполнять тесты вне зависимости от доступности сервиса 2-105 35
    36. Учебный Центр Luxoft www.luxoft.ru/edu Использование mock- и mock- stub-объектов stub - Mock-объекты Mock: объекты, которые заменяют реальный объект в условиях теста и позволяют проверять вызовы своих членов как часть системы или модульного теста содержат заранее запрограммированные ожидания вызовов, которые они должны получить применяются в основном для interaction testing 2-106 Использование mock- и mock- stub-объектов stub - Mock-объекты Во время инициализации теста, мы создаем и настраиваем mock-объект (определяем возвращаемые значения, ожидаемые вызовы и их аргументы) Во время выполнения, mock-объект сравнивает получаемые вызовы и значения с ожидаемыми, и «заваливает» тест при несовпадении По окончании теста проверяется наличие методов, которые не были вызваны 2-107 Использование mock- и mock- stub-объектов stub - Mock-объекты Рассмотрим пример: при удалении пользователя, сообщение об этом должно появляться в логе public void TestRemoveUser() { CommonUser expectedCU = createCommonUser(); UserManagementFacade facade = new UserManagementFacadeImpl(); facade.removeUser(expectedCU.getUserNumber()); assert.False(“User should not exist after removing”, facade.userExists(expectedCU.getUserNumber())); } 2-108 36
    37. Учебный Центр Luxoft www.luxoft.ru/edu Использование mock- и mock- stub-объектов stub - Mock-объекты public void TestRemoveUser_Mock() { CommonUser expectedCU = createCommonUser(); MockAuditLog mockLog = new MockauditLog(); mockLog.setExpectedLogMessage(helper.getDate(), helper.TEST_USER_NAME, helper.USER_REMOVE_ACTION_CODE, expectedCU.getUserNumber); mockLog.setExpectedCallsNumber(1); UserManagementFacade facade = new UserManagementFacadeImpl(); facade.setAuditLog(mockLog); facade.removeUser(expectedCU.getUserNumber()); assert.False(“User should not exist after removing”, facade.userExists(expectedCU.getUserNumber())); mockLog.verify(); } 2-109 Использование mock- и mock- stub-объектов stub - Mock-объекты Реализация метода LogMessage mock-объекта public void LogMessage(Data actualDate, String actualUser, String actualActionCode, int actualUserNumber) { actualCallsNumber++; Assert.assertEquals(“date”, expectedDate, actualDate); Assert.assertEquals(“user”, expectedUser, actualUser); Assert.assertEquals(“action code”, expectedActionCode, actualActionCode); Assert.assertEquals(“number”, expectedNumber, actualNumber); } 2-110 Использование mock- и mock- stub-объектов stub - Подходы к модульному тестированию State-based testing – подход, при котором проверяется состояние объекта после прохождение unit-теста Interaction testing – подход к модульному тестированию, при котором тестируется взаимодействие объектов, поведение методов, последовательность их вызовов и т.п. 2-111 37
    38. Учебный Центр Luxoft www.luxoft.ru/edu Использование mock- и mock- stub-объектов stub - Подходы к модульному тестированию В state-based testing нас интересует, в какое состояние перешел объект после вызова тестируемого метода, или что вернул наш метод и правилен ли этот результат Подобные проверки проводятся при помощи вызова методов класса Assert различных unit-тест фреймворков: Assert.AreEqual(), Assert.IsNull() и т.д. 2-112 Использование mock- и mock- stub-объектов stub - Подходы к модульному тестированию В interaction testing нас интересует прежде всего не статическое состояние объекта, а те динамические вызовы методов, которые происходят у него внутри В этом случае используют специальные mock-фреймворки, содержащие определенные конструкции для записи ожиданий и их последующей проверки (методы Verify(), VerifyAll() и т.п.) 2-113 Использование mock- и mock- stub-объектов stub - Подходы к модульному тестированию Martin Fawler называет эти два подхода классическим (classical) и мокистским (mockist) unit-тестированием и делит программистов на предпочитающих первый и второй подходы На самом деле, иногда просто удобнее проверить состояние объекта, а иногда – его взаимодействие с другими объектами 2-114 38
    39. Учебный Центр Luxoft www.luxoft.ru/edu Использование mock- и mock- stub-объектов stub - Подходы к модульному тестированию Эти два подхода прекрасно уживаются вместе, когда вы понимаете, о чем идет речь, и что именно вы хотите сейчас проверить Точно так же, как уживаются в одном тесте mock’и и stub’ы 2-115 План курса Модуль 1: Модульные тесты Тестирование как способ обеспечения качества продукта Уровни тестирования Цели и задачи модульного тестирования Покрытие кода Унаследованный код Организационные аспекты тестирования 2-116 Понятие покрытия программного кода Понятие полноты системы тестов Одной из оценок качества системы тестов является полнота – величина той части функциональности системы, которая проверяется тестами Полная система позволяет утверждать, что система реализует всю функциональность, указанную в требованиях 2-117 39
    40. Учебный Центр Luxoft www.luxoft.ru/edu Понятие покрытия программного кода Понятие полноты системы тестов Кроме того, это позволяет утверждать, что система не реализует никакой другой функциональности Степень покрытия программного кода тестами – важный количественный показатель, позволяющий оценить качество как системы тестов, так и тестируемой системы 2-118 Понятие покрытия программного кода Понятие полноты системы тестов Одним из наиболее часто используемых методов определения полноты системы тестов является определение отношения количества тест-требований, для которых существуют тесты, к общему количеству тест-требований В данном случае речь идет о покрытии тестами тест-требований 2-119 Понятие покрытия программного кода Понятие полноты системы тестов В качестве единицы измерения степени покрытия здесь выступает процент тест- требований, для которых существуют тесты Покрытие требований позволяет оценить степень полноты системы тестов по отношению к функциональности системы, но не позволяет оценить полноту по отношению к ее программной реализации 2-120 40
    41. Учебный Центр Luxoft www.luxoft.ru/edu Понятие покрытия программного кода Понятие покрытия кода Одна и та же функция может быть реализована при помощи совершенно различных алгоритмов, требующих разного подхода к организации тестирования Для более детальной оценки полноты системы тестов анализируется покрытие программного кода, называемое также структурным покрытием 2-121 Понятие покрытия программного кода Понятие покрытия кода Во время работы каждого тестового примера выполняется некоторый участок программного кода системы При выполнении всей системы тестов выполняются все участки программного кода, которые задействует эта система тестов 2-122 Уровни покрытия Уровни покрытия кода Существует несколько различных способов измерения покрытия, основные из них: покрытие операторов покрытие условий покрытие путей покрытие функций покрытие вход/выход 2-123 41
    42. Учебный Центр Luxoft www.luxoft.ru/edu Уровни покрытия Покрытие операторов Для обеспечения полного покрытия программного кода на уровне операторов необходимо, чтобы в результате выполнения тестов каждый оператор был выполнен хотя бы один раз Перед началом тестирования необходимо выделить переменные, от которых зависит выполнение различных ветвей условий и циклов в коде – управляющие входные переменные 2-124 Уровни покрытия Покрытие операторов if (i == 0 || i == 101) { if (showMessage) { MessageBox.Show(“Входной параметр имеет недопустимое значение ” + i.ToString()); } else { System.Out.Writeln(“Входной параметр имеет недопустимое значение ” + i.ToString()); } return -1; } 2-125 Уровни покрытия Покрытие операторов Для полного покрытия по операторам, достаточно двух тестов: i = 0, showMessage = true i = 0, showMessage = false Легко заметить, что при этом, тесты не покрывают всей функциональности (не протестировано поведение системы при i = 101) 2-126 42
    43. Учебный Центр Luxoft www.luxoft.ru/edu Уровни покрытия Покрытие операторов Также проблемы этого метода покрытия можно увидеть и на примерах других управляющих структур Например, при проверке циклов do … while – при данном уровне покрытия достаточно выполнение цикла только один раз, при этом метод совершенно нечувствителен к логическим операторам || и && 2-127 Уровни покрытия Покрытие операторов Другой особенностью данного метода является зависимость уровня покрытия от структуры программного кода Рассмотрим простейший пример: if (condition) MethodA(); else MethodB(); 2-128 Уровни покрытия Покрытие операторов Если MethodA() содержит 99 операторов, а MethodB() — один оператор, то единственного теста, устанавливающего condition в true, будет достаточно для достижения 99%-го уровня покрытия При этом аналогичный тестовый пример, устанавливающий значение condition в false, даст слишком низкий уровень покрытия (1%) 2-129 43
    44. Учебный Центр Luxoft www.luxoft.ru/edu Уровни покрытия Покрытие условий Для обеспечения полного покрытия условий необходимо: каждая точка входа и выхода в программе и во всех ее функциях должна быть выполнена по крайней мере один раз все логические выражения в программе должны принять каждое из возможных значений хотя бы один раз Таким образом, для покрытия по веткам требуется как минимум два теста 2-130 Уровни покрытия Покрытие условий if (i == 0 || i == 101) { if (showMessage) { MessageBox.Show(“Входной параметр имеет недопустимое значение ” + i.ToString()); } else { System.Out.Writeln(“Входной параметр имеет недопустимое значение ” + i.ToString()); } return -1; } 2-131 Уровни покрытия Покрытие условий Для покрытия предыдущего примера кода по ветвям потребуется уже три теста Это связано с тем, что первый условный оператор if имеет неявную ветвь – пустую ветвь else Для обеспечения покрытия по ветвям необходимо покрывать и пустые ветви 2-132 44
    45. Учебный Центр Luxoft www.luxoft.ru/edu Уровни покрытия Покрытие условий Особенность данного уровня покрытия заключается в том, что на нем могут не учитываться логические выражения, значения которых получаются вызовом методов Рассмотрим пример кода: if (condition1 && (condition2 || Method())) statement1; else statement2; 2-133 Уровни покрытия Покрытие условий Полное покрытие условий может быть достигнуто при помощи двух тестов: condition1 = true, condition2 = true condition1 = false, condition2 = true/false В обоих случаях не происходит вызова метода Method() (хотя покрытие будет полным) Для его проверки необходимо добавить еще один тест: condition1 = true, condition2 = false 2-134 Уровни покрытия Покрытие путей В данном случае считаются все пути, которые выполняются в процессе работы тестируемого метода Путь - уникальная последовательность выполнения операторов, с учетом условных операторов Метод, содержащий в себе N условий, имеет 2^N путей Метод, содержащий цикл, может иметь бесконечное число путей 2-135 45
    46. Учебный Центр Luxoft www.luxoft.ru/edu Уровни покрытия Покрытие путей Т.о. в большинстве случаев 100%-е покрытие путей обеспечить невозможно Для решения этой проблемы, может быть применен метод покрытия основных (базисных, линейно-независимых) путей Основные пути – минимальный набор путей, комбинация которых может обеспечить все возможные пути выполнения метода 2-136 Уровни покрытия Покрытие путей Число таких путей равно числу уникальных условных операторов, увеличенное на 1 Рассмотрим следующий пример: if (condition1) statement1; if (condition2) statement2; if (condition3) statement3; 2-137 Уровни покрытия Покрытие путей Для достижения 100% покрытия основных путей, нам потребуется 4 линейно- независимых пути Первый путь выбирается случайно (пусть это будет путь, когда все условные выражения принимают значение true) Оставшиеся пути получаются поочередным инвертированием одного из условных выражений первого пути 2-138 46
    47. Учебный Центр Luxoft www.luxoft.ru/edu Уровни покрытия Покрытие путей Таким образом, получаем четыре основных пути, которые необходимо покрыть: condition1 condition2 condition3 Path 1 true true true Path 2 false true true Path 3 true false true Path 4 true true false 2-139 Уровни покрытия Покрытие путей В случае наличия циклов, может использоваться следующий подход: выделяем классы путей (к одному классу можно отнести пути, отличающиеся количеством итераций в конкретном цикле) класс считается покрытым, если покрыт хотя бы один путь из него 100% покрытие достигнуто, если покрыты все классы путей 2-140 Уровни покрытия Покрытие функций Покрытие функций – каждая ли функция тестируемого модуля является выполненной хотя бы один раз Является одним из самых простых методов расчета покрытия, и дает довольно общее представление о качестве тестируемого модуля С одной стороны, данное покрытие говорит нам о том, что тестами покрыт весь реализованный функционал модуля 2-141 47
    48. Учебный Центр Luxoft www.luxoft.ru/edu Уровни покрытия Покрытие функций С другой стороны, оно не гарантирует нам адекватное поведение модуля, поскольку: не проверяется реакция функций на все возможные входные параметры не проверяется реакция системы на все возможные возвращаемые функцией значения 2-142 Уровни покрытия Покрытие вход/выход Покрытие вход/выход – все ли возможные варианты вызова функций и возврата из них были выполнены На данном уровне обеспечивается тестирование как самих функций (все возможные варианты вызова), так и их взаимодействие в составе модуля (все возможные варианты возврата) 2-143 Анализ покрытия Цели и задачи анализа К анализу покрытия программного кода можно приступать только после полного покрытия требований Полное покрытие программного кода не гарантирует того, что тесты проверяют все требования к системе Целью анализа полноты покрытия кода является выявление участков кода, которые не выполняются при выполнении тестов 2-144 48
    49. Учебный Центр Luxoft www.luxoft.ru/edu Анализ покрытия Цели и задачи анализа В идеальном случае при полном покрытии функциональных требований должно получаться 100% покрытие кода Однако на практике такое происходит только в случае очень простого кода Причины «недопокрытия» кода могут быть различными 2-145 Анализ покрытия Причины плохого покрытия кода Недостатки в формировании тестов, основанных на требованиях тестовый набор должен быть дополнен недостающими тестами Неадекватности в требованиях требования должны быть модифицированы, после чего разработаны и выполнены дополнительные тесты, покрывающие новые требования 2-146 Анализ покрытия Причины плохого покрытия кода «Мертвый код» этот код должен быть удален, и проведен анализ для оценки эффекта удаления и необходимости перепроверки Дезактивируемый код – код, работающий только в определенных конфигурациях окружения 2-147 49
    50. Учебный Центр Luxoft www.luxoft.ru/edu Анализ покрытия Причины плохого покрытия кода Дезактивируемый код для такого кода должна быть установлена нормальная эксплуатационная среда, в которой он выполняется написаны тесты, покрывающие его написаны тесты, проверяющие, что данный код не может быть преднамеренно выполнен в других конфигурациях 2-148 Анализ покрытия Причины плохого покрытия кода Избыточные условия пример такого условия – выражение !b || (a && b) при b = false, значение переменной a не имеет значения, т.е. условие избыточно и вторая его часть не будет проверяться Защитный код 2-149 Анализ покрытия Причины плохого покрытия кода Защитное программирование - это метод организации программного кода таким образом, чтобы при работе системы последствия проявления дефектов в ней не приводили к сбоям, отказам и авариям (проверка входных данных, обработка исключений и т.д.) 2-150 50
    51. Учебный Центр Luxoft www.luxoft.ru/edu Анализ покрытия Причины плохого покрытия кода Например, это может быть ветка default в операторе выбора switch Входное условие оператора switch может принимать определенные значения Как следствие, ветка default, возможно никогда не будет выполнена 2-151 Анализ покрытия Причины плохого покрытия кода Защитное программирование, как правило, не дает нам никакой информации о том, где в системе находится дефект Его нельзя рассматривать как замену тестирования - эти два аспекта разработки систем лишь дополняют друг друга 2-152 Анализ покрытия Причины плохого покрытия кода Также существуют случаи, когда модульное тестирование кода сильно затруднено, либо вообще невозможно: генерация случайных чисел сложные математические алгоритмы параллельные алгоритмы 2-153 51
    52. Учебный Центр Luxoft www.luxoft.ru/edu Анализ покрытия Результаты анализа Таким образом, отсутствие покрытия каких-либо участков кода может являться сигналом к переработке тестов, кода, а иногда – и требований С другой стороны, 100% покрытие кода не гарантирует нам, что в приложении нет ошибок 2-154 Анализ покрытия Результаты анализа Помните, что тесты пишутся для повышения качества кода и лучшего его понимания, а не для повышения показателей метрик 2-155 План курса Модуль 1: Модульные тесты Тестирование как способ обеспечения качества продукта Уровни тестирования Цели и задачи модульного тестирования Покрытие кода Унаследованный код Организационные аспекты тестирования 2-156 52
    53. Учебный Центр Luxoft www.luxoft.ru/edu Наследованный код и модульные тесты Legacy-код Причины появления legacy-код: Появление новых технологий: существуют огромные программные комплексы, написанные на языке C++, который считался передовым всего десять лет назад 2-157 Наследованный код и модульные тесты Legacy-код Развитие существующих технологий: частые изменения в языках и платформах Java/C# очень быстро приводят к появлению унаследованных решений, поскольку то, что когда-то считалось новаторским, превращается в неподдерживаемый, устаревший код 2-158 Наследованный код и модульные тесты Legacy-код Бурное развитие системы: зачастую, программисты сосредоточивают усилия именно на реализации новых возможностей программного обеспечения – применяются новые технологии, но существующий функционал при этом не совершенствуется 2-159 53
    54. Учебный Центр Luxoft www.luxoft.ru/edu Наследованный код и модульные тесты Legacy-код Итак, нужно ли писать тесты для legacy- кода? Да, нужно, но только если вы собираетесь его изменять: Unit-тесты позволят вам понять, как работает код Unit-тесты позволят вам убедиться, что ваши изменения не нарушили логику работы модуля 2-160 Наследованный код и модульные тесты Legacy-код Ваша цель – получить тесты, которым можно доверять Если функция полностью покрыта модульными тестами, то вы не будете боятся ее изменить или даже полностью переписать 2-161 Наследованный код и модульные тесты Избавление от зависимостей Как правило, такой код имеет множество зависимостей: подключает различные lib и dll, посылает сообщения в сеть или другим компонентам, отображает что-то в GUI и т.д. Итак, первое, что нам необходимо сделать – это избавиться от этих зависимостей Для этого можно использовать уже знакомые нам mock- и stub-объекты 2-162 54
    55. Учебный Центр Luxoft www.luxoft.ru/edu Наследованный код и модульные тесты Dependency injection Зачастую, этих средств недостаточно и необходимо проводить рефакторинг Здесь на помощь нам приходит паттерн “Dependency injection” Суть его заключается в следующем: если класс А использует класс В, то необходимо сделать так, чтобы конкретная реализация класса В передавалась классу А “извне”, а не определялась внутри него 2-163 Наследованный код и модульные тесты Проблемы покрытия legacy-кода После того, как все связи с внешним миром у класса или функции оборваны - мы ее полностью контролируем и можем полностью проверить ее работу Для этого может понадобиться создать еще несколько заглушек, которые будут выдавать нужные данные для конкретных тестов 2-164 Наследованный код и модульные тесты Проблемы покрытия legacy-кода В итоге получается, что надо написать достаточно много дополнительного кода только для того, чтобы написать первый тест Однако для второго теста, такого кода потребуется уже меньше «Десятый» тест уже пишется спокойно, с использованием ранее написанного кода 2-165 55
    56. Учебный Центр Luxoft www.luxoft.ru/edu План курса Модуль 1: Модульные тесты Тестирование как способ обеспечения качества продукта Уровни тестирования Цели и задачи модульного тестирования Покрытие кода Унаследованный код Организационные аспекты тестирования 2-166 Почему разработчики не хотят внедрять u-тесты Слишком простой код Как правило, так кажется только в момент его написания Через полгода (а может и через пару недель) связи этого кода с другими могут показаться не такими очевидными, а сам код – не таким уж и простым 2-167 Почему разработчики не хотят внедрять u-тесты Тесты сложнее, чем сам код Как правило, такая ситуация возникает в двух случаях: низкая квалификация разработчика плохой дизайн тестируемого кода Возникают ситуации, когда протестировать класс практически невозможно – напр. необходимо зарегистрировать пользователя, добавить данные в БД, предоставить config-файлы и т.д. 2-168 56
    57. Учебный Центр Luxoft www.luxoft.ru/edu Почему разработчики не хотят внедрять u-тесты Тесты сложнее, чем сам код Все это говорит о плохом дизайне системы – классы очень сильно зависят друг от друга, иногда нетривиальным образом Новые тесты писать при этом очень сложно, они часто ломаются В этом случае стоит начать написание тестов с простейших классов с минимальным числом зависимостей 2-169 Почему разработчики не хотят внедрять u-тесты Тесты сложнее, чем сам код Каждый класс, как и его тест, должны быть небольшими и понятными После того, как будет покрыт простейший функционал, можно перемещаться на более «высокие» уровни системы Одновременно с этим производить рефакторинг кода для уменьшения связности классов 2-170 Почему разработчики не хотят внедрять u-тесты Недостаточно времени Если сравнивать написание классов с тестами и без, то безусловно, во втором варианте времени понадобится больше Однако давайте посмотрим на процесс разработки и написания тестов более широко 2-171 57
    58. Учебный Центр Luxoft www.luxoft.ru/edu Почему разработчики не хотят внедрять u-тесты Недостаточно времени При наличии модульных тестов, пишется только тот код, который необходим для их успешного выполнения Классы становятся меньше по размеру и выполняют четко определенный набор функций Таким образом, пишется меньше кода 2-172 Почему разработчики не хотят внедрять u-тесты Недостаточно времени Разрабатывая небольшими шагами, вы всегда знаете, что вас ждет дальше – вы двигаетесь без промедления Использование unit-тестов существенно сокращает время, проводимое в отладчике добавив небольшую порцию кода, мы тут же убеждаемся в его работоспособности 2-173 Почему разработчики не хотят внедрять u-тесты Недостаточно времени внеся изменения в классы, мы тут же убеждаемся, что не нарушили работы остальных классов Интеграция модулей проходит легче и быстрее, поскольку каждый из них хорошо оттестирован Уменьшается количество ошибок в коде, а так же время на их поиск и исправление 2-174 58
    59. Учебный Центр Luxoft www.luxoft.ru/edu Почему разработчики не хотят внедрять u-тесты Недостаточно времени Более качественный дизайн полученный в ходе такой разработки, помогает в дальнейшем улучшать код, делая это очень быстро 2-175 Почему разработчики не хотят внедрять u-тесты Не умеют писать тесты В этом нет ничего страшного, т.к. не обязательно сразу начинать тестировать сложные компоненты, используя все возможности xUnit и mock-фреймворков Для начала можно попытаться воспроизвести в тестах действия, которые разработчик делает вручную при тестировании модуля 2-176 Почему разработчики не хотят внедрять u-тесты Не умеют писать тесты Даже небольшая и «неуклюжая» тестовая обвязка намного лучше, чем еще не написанная «гибкая система модульного тестирования, покрывающая 100% функционала» Начинайте с простых приемочных тестов или тестирования классов низших уровней и продвигайтесь далее в глубь системы 2-177 59
    60. Учебный Центр Luxoft www.luxoft.ru/edu Почему разработчики не хотят внедрять u-тесты Двукратное увеличение кода По мнению некоторых разработчиков, модульные тесты – это двукратное увеличение кода Это не так На самом деле оно троекратное (или даже больше) Однако давайте рассмотрим и другие стороны этого вопроса 2-178 Почему разработчики не хотят внедрять u-тесты Двукратное увеличение кода Во-первых, тесты – это код, который досконально проверяет работоспособность вашего код Во-вторых, объем рабочего кода уменьшается Однако, при изменении кода, необходимо изменять и тестирующий код, а это двойная или тройная работа 2-179 Почему разработчики не хотят внедрять u-тесты Двукратное увеличение кода Классы становятся узкоспециализированными с четко определенными интерфейсами Они берут на себя как можно меньше обязанностей, но выполняют их хорошо Снижаются зависимости между классами Благодаря этому изменения, вносимые в систему, носят локальный характер 2-180 60
    61. Учебный Центр Luxoft www.luxoft.ru/edu Почему разработчики не хотят внедрять u-тесты Двукратное увеличение кода Конечно, можно спроектировать систему так, чтобы изначально обязанности классов были распределены оптимальным образом Однако на практике это практически невозможно: никто не может увидеть всей картины целиком требования к системе могут меняться 2-181 План курса Модуль 1: Модульное тестирование Тестирование как способ обеспечения качества продукта Уровни тестирования Цели и задачи модульного тестирования Покрытие кода Унаследованный код Организационные аспекты тестирования 2-182 Вопросы и ответы 61
    62. Учебный Центр Luxoft www.luxoft.ru/edu Практикум Упражнение 1 Разработка приложения по Use-case диаграмме Проработка методологии модульного тестирования Разработка собственных модульных тестов 2-184 Модуль 2 Каркас JUnit Рассматриваемые темы: Введение Основные этапы разработки модульных тестов Простые тесты Запуск тестирования Наборы тестов 2-185 Каркас JUnit JUnit JUnit – open-source продукт JUnit – часть семейства каркасов модульного тестирования xUnit для различных платформ программирования В разработке JUnit участвовал К. Бек, идеолог eXtreme Programming Распространяется одним jar-файлом junit.jar 2-186 62
    63. Учебный Центр Luxoft www.luxoft.ru/edu Каркас JUnit JUnit как каркас Специфика библиотеки – разработчик использует библиотеку Специфика каркаса – каркас использует классы разработчика 2-187 Каркас JUnit Основные положения JUnit Сравнение (assertion) ожидаемых и реальных результатов выполнения модуля Фикстуры – разделяемые между тестами данные и бизнес-логика Наборы тестов для улучшенной организации тестирования Текстовая и графическая среда запуска тестов 2-188 Каркас JUnit Основные положения JUnit Разработчик может тестировать: > Непосредственно модули или их наборы > Реализовать сложную бизнес-логику тестирования 2-189 63
    64. Учебный Центр Luxoft www.luxoft.ru/edu Основные классы каркаса Пакет junit.framework 2-190 Ключевые классы Класс TestCase Разработчик наследуется от класса TestCase для создания своего теста Класс TestCase реализует шаблон разработки Template Это значит, что в нем реализован каркас бизнес-логики, ссылающийся на методы, определенные в потомках 2-191 Ключевые классы Класс TestCase Определяет каркас бизнес-логики Ссылается на абстрактные методы, определяемые в public void run(){ потомках: setUp(); > setUp() runTest(); > Тестовые методы tearDown(); > tearDown() } 2-192 64
    65. Учебный Центр Luxoft www.luxoft.ru/edu Ключевые классы Класс TestSuite Разработчик использует класс TestSiute для формирования набора тестов Наборы тестов могут включать в себя одиночные тесты и другие наборы тестов Реализует шаблон разработки Composite 2-193 Ключевые классы Класс TestSuite 2-194 Ключевые классы Отношения ключевых классов 2-195 65
    66. Учебный Центр Luxoft www.luxoft.ru/edu Каркас JUnit Рассматриваемые темы: Введение Основные этапы разработки модульных тестов Простые тесты Запуск тестирования Наборы тестов 2-196 Каркас JUnit Основные этапы разработки теста Создать свой класс теста – наследник от TestCase В нем определить методы тестирования вида testXXX() Создать фикстуры – наборы данных и логики, используемых при каждом тесте Возможно, объединить несколько тестов в набор Запустить тест (текстовой/графический интерфейс или в среде разработки) 2-197 Каркас JUnit Неполный пример теста 1 import java.util.*; 1 import junit.framework.*; 2 2 public class SimpleTest extends TestCase { 3 3 public void testEmptyCollection() { 4 4 Collection testCollection = new ArrayList(); 5 5 assertTrue( testCollection.isEmpty()); } 6 6 public static void main( String args[] ){ 7 7 junit.textui.TestRunner.run(SimpleTest.class); } } SimpleTest.java 2-198 66
    67. Учебный Центр Luxoft www.luxoft.ru/edu Каркас JUnit Рассматриваемые темы: Введение Основные этапы разработки модульных тестов Простые тесты Запуск тестирования Наборы тестов 2-199 Каркас JUnit Тестируемый класс public class IntCalculator { int plus( int a, int b ) { return a + b; } int minus( int a, int b ) { return a - b; } } IntCalculator.java 2-200 Каркас JUnit Тестируемый класс Сколько модулей мы имеем для тестирования? 2-201 67
    68. Учебный Центр Luxoft www.luxoft.ru/edu Каркас JUnit Класс теста Класс теста наследуется от класса TestCase Класс теста имеет методы тестирования вида testXXX() Название методов произвольно, важен префикс test 2-202 Каркас JUnit Метод теста В одном методе теста разработчик может протестировать как один модуль, так и реализовать сложную бизнес-логику тестирования нескольких модулей 2-203 Каркас JUnit Метод теста В методах тестирования необходимо сравнивать полученные результаты выполнения модуля с ожидаемыми 2-204 68
    69. Учебный Центр Luxoft www.luxoft.ru/edu Каркас JUnit Сравнение Каркас предлагает специальные методы сравнения (assertion) Разработчику теста необходимо только вызвать их, а результаты сравнения будут переданы каркасу автоматически 2-205 Каркас JUnit Методы сравнения Сравнение на эквивалентность assertEquals Сравнение на истинность assertTrue, assert Сравнение на ложность assertFalse Сравнение на null assertNull Сравнение на не-null assertNotNull Сравнение на идентичность assertSame Сравнение на не-идентичность assertNotSame 2-206 Каркас JUnit Методы сравнения 2-207 69
    70. Учебный Центр Luxoft www.luxoft.ru/edu Каркас JUnit Методы сравнения 2-208 Каркас JUnit Методы сравнения Эти методы сравнения перегружены для всех примитивных типов Java и класса Object 2-209 Каркас JUnit Пример метода тестирования 1 1 import junit.framework.*; 2 2 public class IntCalculatorTest extends TestCase { 3 3 public IntCalculatorTest(String s){ super(s); } 4 4 public void testAdd() { 5 5 IntCalculator calc = new IntCalculator(); 6 6 assertEquals( calc.plus(2,2), 4 ); } 7 7 public static void main( String args[] ){ 8 8 junit.textui.TestRunner.run(IntCalculatorTest.class); } } IntCalculatorTest.jav a 2-210 70
    71. Учебный Центр Luxoft www.luxoft.ru/edu Каркас JUnit Ошибки (failures) и сбои (errors) Failure – это ошибки сравнений > Выбрасываются assert-методами Error – сбои, не связанные со сравнениями > Исключения, специфичные для бизнес- логики программы, а не тестирования 2-211 Каркас JUnit Ошибки (failures) и сбои (errors) Failures и Errors передаются каркасу, и он сигнализирует о них тестировщику 2-212 Каркас JUnit Методы тестирования В некоторых случаях необходимо явно сигнализировать каркасу о Failure Для этого предусмотрены специальные методы void fail() void fail( String s ) 2-213 71
    72. Учебный Центр Luxoft www.luxoft.ru/edu Каркас JUnit Фикстуры Фикстура – набор операций, выполняемых до и после тестов Они выполняются для каждого теста testXXX Каковы задачи фикстур? 2-214 Каркас JUnit Фикстуры Фикстуры реализуются методами setUp() tearDown() 2-215 Каркас JUnit Рассматриваемые темы: Основные классы Основные этапы разработки модульных тестов Простые тесты Запуск тестирования Наборы тестов 2-216 72
    73. Учебный Центр Luxoft www.luxoft.ru/edu Каркас JUnit Запуск тестирования Тестирование можно запустить как из самого класса теста, так и из командной строки Для повышения гибкости следует использовать запуск из командной строки 2-217 Каркас JUnit Запуск тестирования Каркас предлагает два класса- launcher для запуска тестов: > Текстовой launcher junit.textui.TestRunner > Графический launcher junit.swingui.TestRunner 2-218 Каркас JUnit Пример запуска тестирования 2-219 73
    74. Учебный Центр Luxoft www.luxoft.ru/edu Каркас JUnit Пример запуска тестирования 2-220 Каркас JUnit Пример запуска тестирования 2-221 Каркас JUnit Пример запуска тестирования 2-222 74
    75. Учебный Центр Luxoft www.luxoft.ru/edu Каркас JUnit Рассматриваемые темы: Основные классы Основные этапы разработки модульных тестов Простые тесты Запуск тестирования Наборы тестов 2-223 Каркас JUnit Наборы тестов – Tests Suites Каркас предлагает возможность объединять тесты в наборы В наборы можно включать как одиночные тесты, так и другие наборы 2-224 Каркас JUnit Наборы тестов – Tests Suites Для создания собственного набора тестов необходимо наследоваться от класса TestSuite В нем необходимо определить статический метод suite(), метод формирования набора 2-225 75
    76. Учебный Центр Luxoft www.luxoft.ru/edu Каркас JUnit Пример набора тестов import junit.framework.*; 1 1 public class AllCalculatorTests extends TestSuite { 2 2 static public Test suite() { 3 3 TestSuite mySuite = new TestSuite(); 4 4 mySuite.addTestSuite( IntCalculatorTest.class ); 5 5 return mySuite; } } AllCalculatorTests.java Какие методы класса теста будут вызваны? 2-226 Каркас JUnit Запуск наборов тестов Набор тестов запускается так же, как и отдельный тест 2-227 Каркас JUnit Пример запуска наборов тестов 2-228 76
    77. Учебный Центр Luxoft www.luxoft.ru/edu Каркас JUnit Пример запуска наборов тестов 2-229 Каркас JUnit Рассмотренные темы: Основные классы Основные этапы разработки модульных тестов Простые тесты Запуск тестирования Наборы тестов 2-230 Практикум Упражнение 2 Разработка Junit-тестов в выработанной методологии 2-231 77
    78. Учебный Центр Luxoft www.luxoft.ru/edu Рефакторинг Содержание Основные понятия Код с душком Каталог рефакторингов Инструменты Литература Что это? это? Рефакторинг (refactoring) (сущ.) – изменение (сущ.) во внутренней структуре программного обеспечения, имеющее целью облегчить обеспечения, понимание его работы и упростить модификацию, без изменения его модификацию, поведения. поведения. Проводить рефакторинг (refactor) (глаг.) – refactor) (глаг.) изменять структуру програмного обеспечения, не затрагивая его поведения. обеспечения, поведения. 78
    79. Учебный Центр Luxoft www.luxoft.ru/edu Зачем проводить рефакторинг? рефакторинг? Улучшает дизайн программного обеспечения Облегчает понимание программного обеспечения Позволяет быстрее добавлять новый функционал Помогает находить ошибки Позволяет писать код быстрее Почему рефакторинг дает результаты? результаты? Пишите код для сегодня, но думаете про завтра сегодня, Трудно модифицировать > “нечитаемые” программы > программы с дублированием кода > программы со сложной логикой условных операторов > работающий код, требующий нового функционала Безопасный путь модификации программ в легко- легко- модифицируемые Когда проводить рефакторинг? рефакторинг? Правило трех > Когда вы делаете одно и то же в третий раз, начинайте рефакторинг При добавлении новой функции > сделайте код более очевидным > как должен был бы быть спроектирован код, чтобы добавить функцию было легко При исправлении ошибок > потому что не смогли увидеть ошибку При разборе кода 79
    80. Учебный Центр Luxoft www.luxoft.ru/edu Как мотивировать руководителя? руководителя? Хороший руководитель (технически-грамотный) технически- грамотный) > ориентирован на качество > хороший способ внедрения – использование при разборе кода Руководитель, подгоняемый графиком Руководитель, > не говорите ☺ > ему все равно как вы укладываетесь в график Долги проектирования (когда мы начнем их оплачивать?) оплачивать?) > не удаляется дублирование кода > ну упрощается код > не проясняется назначение кода “Как долго вы не выплачиваете платежи по кредитам?” кредитам? Проблемы при рефакторинге Не видно, что приводит к росту производительности видно, Рефакторинг баз данных > трудно модифицировать структуру > миграция данных > поддержка нескольких версий БД Рефакторинг XML > поддержка нескольких версий > необходимость модификации внешних клиентов > синхронизация протоколов Изменения интерфейсов > опубликованные SDK и API Изменения архитектуры > Радикальные изменения трудно/невозможно воплотить, используя рефакторинг Когда рефакторинг делать не нужно? нужно? Необходимость переписать все с нуля > когда не удается сделать код устойчивым Близость срока завершения проекта > преимущества появится после срока сдачи 80
    81. Учебный Центр Luxoft www.luxoft.ru/edu Рефакторинг и юнит-тесты юнит- Рефакторинг требует юнит-тестов юнит- > даже автоматизированные рефакторинги могут быть источником ошибок Прежде чем осуществлять рефакторинг фрагмента, сделайте код самотестирующимся фрагмента, Запускайте тесты до и после рефакторинга Рефакторинг и проектирование Не является замена предварительному проектированию Смещаются акценты > цель проектирования найти не единственно правильно решение, а приемлемое Стремление к простоте > гибкие решение сложнее > лучше иметь возможность легко изменять архитектуру Код с душком Метафора для описания мест в проекте, которым проекте, возможно требуется переработка Воспринимается легче, чем “эстетика легче, архитектуры” архитектуры” Чтобы определить “когда” нужен рефакторинг когда” Чтобы определить “когда” остановится когда” 81
    82. Учебный Центр Luxoft www.luxoft.ru/edu Дублирование for (Node childNode = node.getFirstChild();childNode != null;) { String value=node.getTextContent(); if(value!=null && value.trim().length()>0) this.status=value; Node nextChild = childNode.getNextSibling(); ................... } ........................ for (Node childNode = node.getFirstChild();childNode != null;) { String value=node.getTextContent(); if(value!=null && value.trim().length()>0) this.code=value; Node nextChild = childNode.getNextSibling(); ................... } Дублирование Симптомы > практически одинаковые фрагменты кода > фрагменты имеющие одинаковый эффект (на любом концептуальном уровне) Как исправить > выделение метода > шаблонный метод > замещение алгоритма > выделение класса Длинный метод public getLDAPClasses() { if (serverLDAPConf == null) throw new LDAPConnectionException("Server Ldap should not be null."); ldapConnection = init(serverLDAPConf); // login/password local changing if anonymous useActualCredentialsIfAnonymous(serverLDAPConf); ArrayList<String> attrNames = new ArrayList<String>(); Attributes schemaAttrs; try { // it uses getConnectionInfo(), stored in Connection object, // but sets the Username/Passsword to the new specified values isAuthenticated = ldapConnection.authenticateUI(user, password); if (!isAuthenticated) throw new LDAPConnectionException("Not authenticated."); schemaAttrs = ldapConnection.doLDAPSchemaSearch(user, password, objectClass); Attribute mustList = schemaAttrs.get(LDAP_OBJECT_CLASS_MUST_FIELD); if (mustList != null) { addAttrNameToList(attrNames, mustList.getAll()); } Attribute mayList = schemaAttrs.get(LDAP_OBJECT_CLASS_MAY_FIELD); if (mayList != null) { addAttrNameToList(attrNames, mayList.getAll()); } Collections.sort(attrNames); } catch (NamingException nameEx) { Logger.getLogger(this.getClass()).error(ResourceBundleUtils. getFormatingBundleProperty(AdminResourceBundle.adminLog, "request-ldap-failed", nameEx.getMessage())); throw new LDAPConnectionException("Request for LDAP attribute name list failed. See logs."); } return attrNames; } 82
    83. Учебный Центр Luxoft www.luxoft.ru/edu Длинный метод Симптомы > большое количество строк кода в методе Как исправить > выделение метода Большой класс import javax.swing.event.DocumentListener; import static java.awt.BorderLayout.*; ................... 500+ imports ........................... public class ClientFrame extends JFrame { private static final String APP_TITLE = "URLyBird booking system"; private static final int CUSTOMER_COLUMN = 6; ................... 30+ constants ........................... private JPanel searchPanel; private JTextField hotelLocation = createTextField(20); private JTextField hotelName = createTextField(20); private DataSet dataSet = new DataSet(String.class, String.class, Long.class, Boolean.class, Rate.class, Date.class, String.class); private DataTableModel model; private JTable resultTable; private SelectionGroup selectionGroup = new SelectionGroup(); ................... 110+ members ........................... public ClientFrame(String filename) { this(); setUseLiveSearch(true); setData(openDB.getData(filename)); } public ClientFrame(String host, int port) { this(); setUseLiveSearch(false); setData(connect.getData(host, port)); } ................... 205+ methods ........................... } Большой класс Симптомы > большое количество членов класса > большое количество методов класса > большое количество строк кода классе Как исправить > выделение класса > выделение подкласса > выделение интерфейса 83
    84. Учебный Центр Luxoft www.luxoft.ru/edu Длинный список параметров /** * Authenticates agains LDAP v.3 directory */ public boolean authenticate (String hostname, String protocol, int port, String username, String password, Certificate x509, boolean useSsl, boolean bindAnonymously) { .......................................... } Длинный список параметров Симптомы > метод имеет больше 2-4 параметров Как исправить > замена параметра вызовом метода > сохранить целиковый объект > ввести объект-параметр Гипотетическая всеобщность Симптомы > общие решения для будущего > реально не используются (“про запас)” > слишком сложное решение для текущей задачи Как исправить > Сворачивание иерархии > Встраивание класса > Переименование метода 84
    85. Учебный Центр Luxoft www.luxoft.ru/edu Цепочки сообщений Симптомы > Вызовы типа: a.b().c().d(); Как исправить > выделение метода > перемещение метода > сокрытие делегата Комментарии // creating cert path // and getting the first issuer List<X509Certificate> certPathList = new ArrayList<X509Certificate>(); certPathList.add(peerCertificate); ………………………………………………. if (firstIssuer == null) { // we dont't trust the peer cert at all ………………………………. } if (!firstIssuer.equals(peerCertificate)) { // need to search in the trusted CA for the rest of the cert path X509Certificate curCert = firstIssuer; …………………………………… } //remove most-trusted CA from path if (certPathList.size() > 0) certPathList.remove(certPathList.size() - 1); Комментарии Симптомы > большое количество комментариев в коде > “что”, а не “почему” Как исправить > выделение метода > переименование метода > введение утверждения 85
    86. Учебный Центр Luxoft www.luxoft.ru/edu Альтернативные классы с разными интерфейсами class DateUtils { Date nowUtc(int offset) { ................. } } class Utils { Date now() { ................... } } Альтернативные классы с разными интерфейсами Симптомы > Существует 2 и более реализации одного и того же, но с разным интерфейсом Как исправить > переименование метода > перемещение метода > выделение родительского класса Временное поле public class Balance { private int noOfPeopleInDepartment; private int noOfPeopleInOfshore; public calculateBalance(Department dep) { noOfPeopleInDepartment=dep.getHeadCount(); .................. } } 86
    87. Учебный Центр Luxoft www.luxoft.ru/edu Временное поле Симптомы > значение поля устанавливается только в определенных условиях Как исправить > выделение класса Ленивый класс class Male extends Human { String getType() { return "M"; } } Ленивый класс Симптомы > класс не выполняет никакой работы Как исправить > свертывание иерархии > встраивание класса 87
    88. Учебный Центр Luxoft www.luxoft.ru/edu Операторы switch switch(period) { case: SUMMER_PERIOD { rate=rate*0.7-monthDiscount; } case: HOLIDAY_PERIOD { rate=rate*0.6; } case: NORMAL_PERIOD { rate=rate-monthDiscount; } } Операторы switch Симптомы > switch выражения > много идущих подряд if, else-if > instanceof Как исправить > не эмулируйте наследование > выделение метода > перемещение метода, замена кода типа подклассом или стратегией/состоянием > замена условных выражений полиморфизмом > замена параметра явными методами > введение null-объекта Группы данных public class Scheduler { private Date startOfWorkPeriod; private Date endOfWorkPeriod; private Date rateOfWorkPeriod; private Date startOfSupportPeriod; private Date endOfSupportPeriod; private Date rateOfSupportPeriod; ……………………………… } 88
    89. Учебный Центр Luxoft www.luxoft.ru/edu Группы данных Симптомы > одни и те же параметры часто встречаются вместе как поля классов или аргументы методов > группы названий полей содержат одинаковые подстроки Как исправить > выделение класса > введение объекта-параметра > сохранение целого объекта > перемещение метода Одержимость элементарными типами final int static MONDAY=1; final int static TUESDAY=2; final int static WEDNESDAY=2; //calculate price in USD double price=hourRate*hours; double priceInUSD=price*USD_TO_RUR_RATE; Одержимость элементарными типами Симптомы > использование примитивных типов для работы со структурными данными > перечисления и константы представляются с помощью int > строковые название представляют названия полей Как исправить > замена значения данных объектом > замена кода типа классом > замена кода типа подклассами > замена кода типа состоянием/стратегией > выделение классов > введение объекта-параметра > замена массива объектом 89
    90. Учебный Центр Luxoft www.luxoft.ru/edu Классы данных public class DataClass { private Date start; private Date end; private double rate; public Date getStart() { return start; } public void setStart(Date start) { this.start = start; } public Date getEnd() { return end; } public void setEnd(Date end) { this.end = end; } public double getRate() { return rate; } public void setRate(double rate) { this.rate = rate; } } Классы данных Симптомы > содержат только данные и методы для их получения и установки > нет поведения Как исправить > инкапсуляция поля > инкапсуляция коллекции > удаление метода установки значения > перемещение метода > сокрытие метода Посредник public class Director { Calendar scheduler; public void scheduleMeeting(Date start) { scheduler.scheduleMeeting(start); } public void scheduleCall(Date start) { scheduler.scheduleCall(start); } …………………………………………. } 90
    91. Учебный Центр Luxoft www.luxoft.ru/edu Посредник Симптомы > класс делегирует выполнение более половины методов Как исправить > удаление посредника > замена делегирования наследованием Отказ от наследства Симптомы > подкласс не использует данные или метода родителя > использование данных или методов родителя связано с проблемами Как исправить > замена наследование делегированием > выделение подкласса > спуск поля > спуск метода Завистливые функции class Processor { private Queue executionQueue; public void scheduleExecutionOrder() { int pos=executionQueue.getCurrentPosition(); if(pos=executionQueue.length()) executionQueue.increaseBuffer(POOL_STEP); executionQueue.add(task); } } 91
    92. Учебный Центр Luxoft www.luxoft.ru/edu Завистливые функции Симптомы > метод манипулирует больше данными другого класса, чем того где объявлен Как исправить > перемещение метода > выделение метода Расходящиеся модификации Симптомы > часто приходится модифицировать класс по совершенно разным причинам Как исправить > выделение класса > выделение подкласса > выделение суперкласса Стрельба дробью Симптомы > для внесения каждого изменения необходимо поменять несколько классов Как исправить > встраивание класса > перемещение метода > перемещение поля 92
    93. Учебный Центр Luxoft www.luxoft.ru/edu Рефакторинги Композиция методов Перераспределение функций между объектами Организация данных Работа с условной логикой Упрощение вызовов Обобщение Композиционные Выделение метода Встраивание метода Встраивание временной переменной Замена временной переменной вызовом Введение поясняющей переменной Разбиение временной переменной Удаление присваиваний параметрам Замена метода объектом методов Замещение алгоритма Выделение метода (extract method) Когда: есть фрагмент кода, который можно сгруппировать for (…..) { for (…..) String value=node.getTextContent(); { String value=node.getTextContent(); if(!isEmpty(value)) this.status=value; if(value!=null && value.trim().length()>0) this.status=value; ................... ……………..................... } } public boolean isEmpty(String str) { return value!=null && value.trim().length()>0; } 93
    94. Учебный Центр Luxoft www.luxoft.ru/edu Встраивание метода (inline method) Когда: тело метода также очевидно как его название public int getStatus() { return (lengthMoreThanLimit(record)) ? 2 : 1; public int getStatus() } { private void lengthMoreThanLimit(String record) return (record.length()>10) ? 2 : 1; { } return record.length() > 10; } Встраивание временной переменной (inline temp) Когда: есть временная переменная, которой присваивается значение один раз, переменная мешает дальнейшим рефакторингам public void calculateResult() public void calculateResult() { { int result = calculator.power(x, y); return calculator.power(x, y); return result; } } Замена временной переменной вызовом (Replace Temp with Query) Когда: временная переменная используются для хранения значения выражения public void balance() { if (basePrice() > 1000) public void balance() return basePrice() * 0.95; { else double basePrice = quantity * itemPrice; return basePrice() * 0.98; if (basePrice > 1000) } return basePrice * 0.95; else private double basePrice() return basePrice * 0.98; { } return quantity * itemPrice; } 94
    95. Учебный Центр Luxoft www.luxoft.ru/edu Введение поясняющей переменной (introducing explaining variable) Когда: есть сложное выражение boolean isMac = platform.toUpperCase().indexOf("MAC") > -1; if(platform.toUpperCase().indexOf("MA C")>-1 && double isIE = browser.toUpperCase().indexOf("IE"); browser.toUpperCase().indexOf("I E")>-1 && boolean canResize = resize > 0; wasInitizalized() && resize>0) { if(isMac && isIE >-1 && wasInitizalized() && canResize) ...................... { } ...................... } Разбиение временной переменной (split temporary variable) Когда: есть временная переменная, которой много раз присваиваются значения, но не переменная цикла и не накопление результата public void printInfo() public void printInfo() { double res = 2 * (height + width); { System.out.println(res); double perimeter = 2 * (height + width); System.out.println(perimeter); res = height * width; System.out.println(res); double area = height * width; } System.out.println(area); } Удаление присваиваний параметрам (remove assignments to parameters) Когда: код выполняет присваивание параметру public void doIt(int input,int quantity) public void doIt(int input,int quantity) { { int result=input; if(quantity>1000) input=input-2; if(quantity>1000) } result=result-2; } 95
    96. Учебный Центр Luxoft www.luxoft.ru/edu Замена метода объектом методов (replace method with method object) (1) Когда: есть длинный метод, в котором локальные переменные не позволяют применить “выделение метода” class Order { public int price(int input) { double primaryBasePrice; double secondaryBasePrice; double tertiaryBasePrice; int importantValue=primaryBasePrice*quantity*input; int importantValue2=secondaryBasePrice+quantity*input; if(input>1000) importantValue=importantValue-tertiaryBasePrice*input; int importantValue3=importantValue2+tertiaryBasePrice*quantity; ......................... } } Замена метода объектом методов (replace method with method object) (2) class PriceCalculator { class Order Order source; { int value; public int price(int input) { double primaryBasePrice; return double secondaryBasePrice; new PriceCalculator(this, double tertiaryBasePrice; input, primaryBasePrice, int importantValue; secondaryBasePrice, int importantValue2; tertiaryBasePrice).compute(); int importantValue3; } } int compute() { ..................... } } Замещение алгоритма (substitute algorithm) Когда: найден более понятный способ решения public String find(String[] people) { for(int i=0;i<people.length;i++) { public String find(String[] people) if(people[i].equals("Don")) { return "Don"; List candidates = Arrays.asList({"Don", "John", "Ken"}); if(people[i].equals("John")) return "John"; for (int i = 0; i < people.length; i++) if(people[i].equals("Mark")) if(candidates.contains(people[i])) return "Mark"; } return people[i]; return ""; return ""; } } 96
    97. Учебный Центр Luxoft www.luxoft.ru/edu Перераспределение функций между объектами Перемещение метода Перемещение поля Выделение класса Встраивание класса Сокрытие делегирования Удаление посредника Введение внешнего метода Введение локального расширения Перемещение метода (move method) Когда: метод чаще использует функции другого класса, а не того где определен Перемещение поля (move field) Когда: поле используется другим классом чаще, чем тем в котором определено 97
    98. Учебный Центр Luxoft www.luxoft.ru/edu Выделение класса (extract class) Когда: класс выполняет работу, которую следует разделить на два класса Встраивание класса (inline class) Когда: класс выполняет слишком мало функций Сокрытие делегирования (hide delegate) Когда: клиент обращается к делегируемому классу объекта. person.getDepartment().getManager() 98
    99. Учебный Центр Luxoft www.luxoft.ru/edu Удаление посредника (remove middle man) Когда: клиент слишком занят простым делегированием Client Class Person +getManager() Department +getManager() Введение внешнего метода (introduce foreign method) Когда: необходимо ввести в класс дополнительный метод, но отсутвует возможность модификации класса Date next=nextDay(previous); Date next=new Date(previous.getYear(), private static Date nextDay(Date now) previous.getMonth(), { previous.getDate()+1); return new new Date(now.getYear(), now.getMonth(), now.getDate()+1); } Введение локального расширения (introduce local extension) Когда: необходимо ввести в класс несколько дополнительных методов, но отсутвует возможность модификации класса Client Class +nextDay(in Date) +nextMonth(in Date) +nextYear(in Date) 99
    100. Учебный Центр Luxoft www.luxoft.ru/edu Организация данных Самоинкапсуляция поля Замена значения данных объектом Замена значения ссылкой Замена ссылки значением Замена массива объектом Дублирование видимых данных Замена однонаправленной связи двунаправленной Замена двунаправленной связи однонаправленной Замена волшебного числа константой Инкапсуляция поля Инкапсуляция коллекции Замена записи классом данных Замена кода типа классом Замена кода типа подклассами Замена кода типа стратегиями/состоянием стратегиями/ Замена подкласса полями Самоинкапсуляция поля (self encapsulated field) Когда: обращение к полю происходит непосредственно private int low,high; private int low,high; public boolean includes(int value) public boolean includes(int value) { { return value>=getLow() && value<=getHigh(); return value>=low && value<=high; } } public int getLow() {return low;} public int getHigh() {return high;} Замена значения данных объектом (replace data value with object) Когда: есть элемент данных, для которого требуется дополнительные данные или поведение 100
    101. Учебный Центр Luxoft www.luxoft.ru/edu Замена значения ссылкой (change value to reference) Когда: есть много одинаковых экземляров класса, которые требуется заменить одним объектом Замена ссылки значением (change reference to value) Когда: есть маленький неизменяемый и неудобный в управлении объект ссылки Замена массива объектов (replace array with object) Когда: есть массив, элементы которого представляют собой разные сущности String data[] = new String[3]; Perfomance perf=new Perfomance(); data[0] = "Liverpool"; perf.setName("Liverpool"); data[1]="15"; perf.setWins(15); 101
    102. Учебный Центр Luxoft www.luxoft.ru/edu Дублирование видимых данных (duplicate observed data) data) Когда: есть данные предметной области приложения, которые существуют только в GUI классах, но к ним нужен доступ методам предметной области приложения Замена однонаправленной связи двунаправленной (change unidirectional association to bidirectional) Когда:есть два класса, которые должны пользоваться методами друг друга, но ссылка между ними есть только в одном направлении Замена двунаправленной связи однонаправленной (change bidirectional association to unidirectional) Когда: имеет двунаправленная связть, но одному из классов больше не нужны методы второго 102
    103. Учебный Центр Luxoft www.luxoft.ru/edu Замена волшебного числа константой (replace magic number with symbolic constant) Когда: есть массив, элементы которого представляют собой разные сущности public double potentialEnergy(double mass,double height) { return mass * 9.81 * height; } static final double GRAVITATION_CONSTANT=9.81; public double potentialEnergy(double mass,double height) { return mass * GRAVITATION_CONSTANT * height; } Инкапсуляция поля (encapsulate field) Когда: есть открытое поле private int low,high; public int low,high; public boolean includes(int value) public boolean includes(int value) { { return value>=getLow() && value<=getHigh(); return value>=low && } value<=high; } public int getLow() {return low;} public int getHigh() {return high;} Инкапсуляция коллекции (encapsulate collection) Когда: метод возвращает коллекцию 103
    104. Учебный Центр Luxoft www.luxoft.ru/edu Замена записи классом данных (replace record with data class) Когда: требуется взаимодействие со структурой в традиционном стиле class DataRecord { private int id; private String name; struct DataRecord public DateRecord(int id, String name) { int id; { String name; this.id = id; } this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name;} public void setName(String name) { this.name = name; } } Замена кода типа классом (replace type code with class) Когда: у класса есть числовой тип, который не влияет на его поведение Person 1 BloodGroup -O : BloodGroup -A : BloodGroup -B : BloodGroup -AB : BloodGroup Замена кода типа подклассами (replace type code with subclasses) Когда: имеется неизменяемый код типа, который влияет на поведение класса Employee Engineer Salesman 104
    105. Учебный Центр Luxoft www.luxoft.ru/edu Замена кода типа стратегиями/состоянием (replace type code стратегиями/ with state/strategy) state/strategy) Когда: имеется неизменяемый код типа, который влияет на поведение класса, но нельзя применить создание подклассов Замена подкласса полями (replace subclass with fields) fields) Когда: есть подклассы, которые различаются только методами, возвращающими данные-константы Работа с условной логикой Декомпозиция условного оператора Консолидация условного выражения Консолидация дублирующихся условных фрагментов Удаление управляющего флага Замена вложенных условных операторов граничным оператором Замена условного оператора полиморфизмом Введение null-объекта null- Введение утверждения 105
    106. Учебный Центр Luxoft www.luxoft.ru/edu Декомпозиция условного оператора (decompose conditional) Когда: имеет сложная цепочка условных проверок (if-then-else) if(date.before(SUMMER_START) || date.after(SUMMER_END)) if(notSummer(date)) charge=quantity*winterRate+winterServiceCharge; charge=winterCharge(quantity); else else charge=quantity*summerRate; charge=summerCharge(quantity); Консолидация условного выражения (consolidate conditional expression) Когда: есть ряд проверок, дяющих одинаковый результат public double accountStatus() { if(seniority<2) return 0; public double accountStatus() if(monthDisabled>12) return 0; { if(isPartTime) return 0; if(isNotEligable()) return 0; ...................................... } } Консолидация дублирующихся условных фрагментов (consolidate duplicate conditional fragments) fragments) Когда: один и тот же фрагмент кода присутствует во всех ветвях условного выражения double total; if (isDiscountDeal()) { total = price * 0.94; if (isDiscountDeal()) send(); total = price * 0.94; } else else total = price * 0.98; { total = price * 0.98; send(); send(); } 106
    107. Учебный Центр Luxoft www.luxoft.ru/edu Удаление управляющего флага (remove control flag) flag) Когда: используется переменная, действующая как управляющий флаг для ряда булевых выражений boolean done=false; while(true) { while(!done) if(rateLimitExceeded()) { if(rateLimitExceeded()) { { .... .... break; done=true; } } if(isOverdrawn()) if(isOverdrawn()) { { ..... ..... done=true; break; } } else {........} else {.....} } } Замена вложенных условных операторов граничным оператором (replace nested conditionals with guard clauses) clauses) Когда: есть условное поведение, из которого неясен нормальный путь выполнения double getPayAmount() { double result; if(isDead) result=deadAmount(); double getPayAmount() else { { if(isSeparated) result=separatedAmount(); if(isDead) return deadAmount(); else if(isSeparated) return separatedAmount(); { if(isRetired) return retiredAmount(); if(isRetired) result=retiredAmount(); return normalAmount(); else result=normalAmount(); } } } return result; } Замена условного оператора полиморфизмом (replace conditional with polymorphism) polymorphism) Когда: есть условный оператор, поведение которого зависит от типа объекта double getSpeed() { switch(type) { Bird case EUROPEAN: return baseSpeed(); +getSpeed() case AFRICAN: return baseSpeed()- loadFactor()*cocunuts; case NOWWEGIAN_BLUE: return isNailed ? 0 : European African Norwegian Blue baseSpeed(voltage); } +getSpeed() +getSpeed() +getSpeed() throw new UnsupportedException ("Unknown type, curerntly unsupported"); } 107
    108. Учебный Центр Luxoft www.luxoft.ru/edu Введение null-объекта (introduce null object) null- Когда: есть многкратные проверки на совпадение значения с null. if(customer==null) plan=BillingPlan.BASIC; else plan=customer.getPlan(); Введение утверждения(introduce assertion) утверждения(introduce Когда: некоторая часть кода предполагает определенное сотояние программы double getExpenseLimit() double getExpenseLimit() { { Assert.isTrue(expenseLimit!=NULL_EXPENSE || return (expenceLimit != NULL_EXPENSE) ? primaryProject!=null); expenceLimit : return (expenceLimit != NULL_EXPENSE) ? primaryProject.getMemberExpenseLimit(); } expenceLimit : primaryProject.getMemberExpenseLimit(); } Упрощение вызовов Переименование метода Добавление параметра Удаление параметра Разделение запроса и модификатора Параметризация метода Замена параметра явными методами Сохранение целого объекта Замена параметра вызовом метода Введение объекта-параметра объекта- Удаление метода установки значения Сокрытие метода Замена конструктора фабричным методом Инкапсуляция нисходящего преобразования типа Замена кода ошибки исключением Замена исключения проверкой 108
    109. Учебный Центр Luxoft www.luxoft.ru/edu Переименование метода (rename method) Когда: имя метода не раскрывает его назначения Добавление параметра (add parameter) Когда: имя метода не раскрывает его назначения Удаление параметра (remove parameter) Когда: параметр больше не используется в теле метода 109
    110. Учебный Центр Luxoft www.luxoft.ru/edu Разделение запроса и модификатора (separate query from modifier) modifier) Когда: метод возвращает значение и изменяет состояние объекта Параметризация метода (parametrize method) method) Когда: несколько методов выполняют сходные действия, но с разными значениями, содержащимися в теле метода Замена параметра явными методами (replace parameter with explicit methods) methods) Когда: есть метод, выполняющий разный код в зависимости от значения параметра перечисляемого типа public void setValue(String name, int value) { public void setHeigth(int value) if (name.equals("height")) { height = value; heigth = value; else if (name.equals("width")) } width = value; public void setWidth(int value) throw new UnsupportedOperationException("Unknown { parameter."); width = value; } } 110
    111. Учебный Центр Luxoft www.luxoft.ru/edu Сохранение целого объекта (preserve whole object) object) Когда: от объекта получаются несколько значений, которые потом передаются в метод как параметры Range range=daysTempRange(); int low=range.getLow(); int high=range.getHigh(); withinPlan = plan.withinPlan(low, high); withinPlan = plan.withinPlan(dayTempRange()); Замена параметра вызовом метода (replace parameter with method) method) Когда: результат метода передается в качестве параметра в новый метод int basePrice=quantity*itemPrice; discount=discountLevel(); finalPrice = discountedPrice(basePrice, discount); int basePrice=quantity*itemPrice; finalPrice = discountedPrice(basePrice); public int discountedPrice(int basePrice) { dicount=discountLevel(); ........................ } Введение объекта-параметра (introduce объекта- parameter object) object) Когда: есть группа параметров, естественным образом связанных друг с другом Customer +amountInvoicedIn(in DateRange) +amountReceivedIn(in DateRange) +amountOverdueIn(in DateRange) 111
    112. Учебный Центр Luxoft www.luxoft.ru/edu Удаление метода установки значения (remove setting method) method) Когда: поле устанавливается в момент создания и больше никогда не меняется Сокрытие метода (hide method) method) Когда: метод не используется никаким другим классом Замена конструктора фабричным методом (replace constructor with factory method) method) Когда: создание объекта подразумевает дополнительные действия static Employee create(int type) { if(type=ENGINEER) Employee(int type) { return new Engineer(); this.type=type; else if(type=SALESMAN) } return new Salesman(); throw new IllegalArgumentException("Unknow employee type"); } 112
    113. Учебный Центр Luxoft www.luxoft.ru/edu Инкапсуляция нисходящего преобразования типа (encapsulate downcast) downcast) Когда: метод возвращает объект, к которому клиент должен применить нисходящее преобразование типа Reading=(Reading)lastReading(); Object lastReading() Reading lastReading() { { return return (Reading)readings.lastElement(); readings.lastElement(); } } Замена кода ошибки исключением (replace error code with exception) exception) Когда: метод возвращает особый код, представляющий ошибку int withdraw(int amount) { void withdraw(int amount) if (amount > balance) { return -1; else if (amount > balance) { throw new BalanceException(); balance -= amount; return 0; balance-=amount; } } } Замена исключения проверкой (replace exception with test) test) Когда: выбрасывается исключение при невыполнении предусловия double getValueOfPeriod(int number) { try { double getValueOfPeriod(int number) return values[number]; { } if(number >= values.length) catch(ArrayIndexOutOfBound) return 0; { return 0; } return values[number]; } } 113
    114. Учебный Центр Luxoft www.luxoft.ru/edu Обобщение Подъем поля Подъем метода Подъем тела конструктора Спуск метода Спуск поля Выделение подкласса Выделение родительского класса Выделение интерфейса Свертывание иерархии Формирование шаблона метода Замена наследования делегированием Замена делегирования наследованием Подъем поля (pull up field) Когда: в подклассах есть одинаковое поле Employee -name Salesman Engineer Подъем метода (pull up method) Когда: в подклассах есть методы с идентичными результатами Employee Salesman Engineer +getName() +getName() 114
    115. Учебный Центр Luxoft www.luxoft.ru/edu Подъем тела конструктора (pull up constructor body) body) Когда: имеются конструкторы с почти идентичными телами public Manager(String name,String id,int grade) public Manager(String name,String id,int grade) { this.name=name; { this.id=id; super(name, id); this.grade=grade; this.grade=grade; } } Спуск метода (push down method) method) Когда: в родительском класса есть поведение, относящиеся только к некоторым из подклассов Employee Salesman Engineer +getQuota() Спуск поля (push down field) Когда: есть поле, используемое лишь некоторыми подклассами Employee Employee -quota Salesman Engineer Salesman Engineer -quota 115
    116. Учебный Центр Luxoft www.luxoft.ru/edu Выделение подкласса (extract subclass) subclass) Когда: в классе есть функции используемые только в некоторых случаях Job Item +getTotalPrice() +getUnitPrice() Labor Item +getUnitPrice() +getEmployee() Выделение родительского класса (extract superclass) superclass) Когда: имеются два класса со сходными функциями Выделение интерфейса Когда: несколько клиентов пользуются одним и тем же подмножеством интерфейса класса или в других классах часть интерфейса является общей “interface” Billable +getRate() +hasSpecialSkill() Employee +getRate() +hasSpecialSkill() +getName() +getDepartment() 116
    117. Учебный Центр Luxoft www.luxoft.ru/edu Свертывание иерархии (collapse hierarchy) hierarchy) Когда: родительский класс и подкласс мало чем различаюся Формирование шаблона метода (form template method) method) Когда: есть два метода в подклассах, выполняющих аналогичные шаги в одинаковом порядке, однако сами шаги различны return getBaseAmount()+getTaxAmount(); Site +getBilling() +getTaxAmount() +getBaseAmount() Residential Site Lifeline Site +getBaseAmount() +getTaxAmount() +getTaxAmount() +getBaseAmount() Замена наследования делегированием (replace inheritance with delegation) delegation) Когда: подкласс использует только часть интерфейса родительского класса или не желает наследовать данные 117
    118. Учебный Центр Luxoft www.luxoft.ru/edu Замена делегирования наследованием (replace delegation with inheritance) inheritance) Когда: вы пользуетесь делегированием и часто пишете много простых делегирований для интерфейса в целом Инструментальные средства рефакторинга С++ > XRefactory (Emacs, XEmacs) > *nix, windows, mac > method extraction; > namespaces rename, > Class rename > Parameters rename, > Variables rename, > fields (structure records) and methods (functions) rename > parameters insertion, deletion and moving. Инструментальные средства рефакторинга (2) С++ > Refactor! For Visual C++ 2005 > Add Block Delimiters > Case to Conditional > Conditional to Case > Create Overload > Encapsulate Field > Extract Method (in class) > Extract Function (outside of class) > Introduce Constant > Move Method to Header > Remove Block Delimiters > Reorder Parameters > Move Method to Source File > Reverse Conditional > Simplify Conditional > Widen Scope > Rename (namespace, type, members, parameters, locals, and macros) 118
    119. Учебный Центр Luxoft www.luxoft.ru/edu Инструментальные средства рефакторинга (3) Delphi 2005 > Rename refactoring > Refactor driven “Find References” > Introduce Variable refactoring > Introduce Field refactoring > Inline Variable refactoring > Change Parameters refactoring > Safe Delete refactoring > Push Members Up / Down refactoring > Pull Members Up refactoring > Extract Superclass refactoring > Extract Interface refactoring > Move Members refactoring > Declare variable refactoring > Declare field refactoring > Extract method refactoring > Find unit/import namespace refactoring > Refactor driven “Find in Files” > Extract to resource string refactoring Литература Мартин Фаулер, “Рефакторинг, улучшение Фаулер, Рефакторинг, существующего кода” кода” Джошуа Кириевски, “Рефакторинг с Кириевски, использованием шаблонов” шаблонов” Стив Макконнелл, “Совершенный код. Макконнелл, код. Практическое руководство по разработке программного обеспечения” обеспечения” Вопросы? Вопросы? Q&A 119
    120. Учебный Центр Luxoft www.luxoft.ru/edu Практикум Упражнение 3 Up-front design VS Simple design Up- Подход Test-first + Refactoring Test- Еще раз разработать приложение по Use-case Use- диаграмме, но с применением подхода Test-first + диаграмме, Test- Refactoring Test Driven Development Содержание Основные понятия и принципы TDD Шаблоны разработки тестов Общие рекомендации Литература 120
    121. Учебный Центр Luxoft www.luxoft.ru/edu Что такое unit-test? unit- Программы, которые тестируют другие программы Программы, > маленькие > запускаются наборами > запускают кусок функционала в заданных условиях > проверяют результат или поведение unit-тесты технически unit- Программная среда для написания юнит-тестов юнит- > написание тестов > утверждения > группировка тестов в наборы Среда для запуска > GUI > встраивание в цикл сборки > интеграция в IDE С++ > CPPUnit Delphi > DUnit Что такое TDD? TDD? Дисциплина разработки программного обеспечения Предсказуемый > вы знаете когда считать работу законченной > не беспокоитесь о нескончаемой череде ошибок “Чистый код, который работает” код, работает” Ваши коллеги могут положится на вас, а вы на вас, них Такой код писать гораздо приятнее ☺ Способ “управления страхом” при разработке страхом” программного обеспечения 121
    122. Учебный Центр Luxoft www.luxoft.ru/edu Почему меня должно это волновать? волновать? Но ведь написание тестов это дополнительные затраты времени? времени? Да, иногда тестового кода больше чем работающего, но: Да, работающего, но: > вы имеет самотестирующийся код => меньше ошибок > при достаточно низкой плотности дефектов, QA команда может перейти от реактивного тестирования к превентивному > уменьшается количество неприятных сюрпризов, менеджерам легче оценивать трудозатраты и привлекать клиентов к совместной работе > при низкой плотности дефектов – можно каждый день получать интегрированную стабильную сборку продукта > вам легче делать рефакторинг и изменять код TDD – это для меня? меня? TDD не для вас, если вас, > вам нравится лепить более-менее работающие куски кода > все заработало и вы надеетесь, что вам не придется возвращаться к этому коду в дальнейшем TDD для тех: тех: > у кого формируется эмоциональная привязанность к коду > кто считает, что чем чище код, тем вероятнее успех Как объяснить это руководителю? руководителю? Проблема: как убедить выделить время на Проблема: написание тестов? тестов? Хороший руководитель (технически-грамотный) технически- грамотный) > ориентирован на качество Руководитель, подгоняемый графиком Руководитель, > не говорите > используйте как персональную практику, вы все равно станете быстрее 122
    123. Учебный Центр Luxoft www.luxoft.ru/edu Цикл разработки TDD красный-зеленый-рефакторинг – мантра TDD красный- зеленый- (ритм) ритм) красный - напишите тест, который не работает тест, или даже не компилируется зеленый – заставьте тест работать как можно быстрее, напишите ровно столько, чтобы он быстрее, столько, сработал рефакторинг – удалите из написанного все дублирование Шаблоны Шаблоны разработка основанной на тестах Шаблоны красной полосы Шаблоны тестирования Шаблоны зеленой полосы Шаблоны xUnit Шаблоны разработки, основанной на тестах разработки, Изолированный тест Список тестов Вначале тест Вначале утверждение результата Тестовые данные Понятные данные 123
    124. Учебный Центр Luxoft www.luxoft.ru/edu Изолированный тест (isolated test) Каким образом исполнение одного теста должно повлиять на выполнение другого теста? теста? > Никаким. Проблема: ломается что-то одно, а остальные Проблема: что- одно, тесты падают по цепочке. цепочке. Изоляция позволяет запускать только подмножество тестов, а не все. тестов, все. Список тестов (test list) Что необходимо тестировать? тестировать? Выпишите все тесты, которые вам нужно написать тесты, для решения сегодняшних задач Зафиксируйте их в виде пустых тестов > не пишите реализацию тестов заранее Вначале тест (test first) first) В какой момент написать тест? тест? > перед тем как вы приступите к написанию нового кода TDD – методика формирования дизайна вашего приложения > как будет использоваться ваш новый код > когда необходимо остановится 124
    125. Учебный Центр Luxoft www.luxoft.ru/edu Вначале утверждение результата (assertion first) С чего начать написание теста? теста? > с оператора assert, который проверяет результат > понимание того, что же будет в результате > начальная точка для размышления Самоподобие > с чего начать разработку системы – с формулировки требований > с чего начать писать функционал – с теста Тестовые данные (test data) data) Какие данные использовать для тестов? тестов? > данные, которые делают тест понятными > думайте о других > если нет разницы между 124 и 1, используйте 1 – используйте осмысленные данные Реалистичные данные > если вы тестируете реакция на внешние события > сравниваете вывод между версиями программного обеспечения > изменяете код, имитирующий реальных процесс Понятные данные (evident data) data) Как отразить в тесте назначение тех или иных данных? данных? > добавьте в тест ожидаемый и реальный результат и попытайтесь сделать отношения между ними понятными > исключение по отношению к “магическим константам” > облегчает программирование Data now=new Date(NOVEMBER_15) Date result=scheduler.scheduleMeeting(now, STANDARD_TOLERANCE, WEEKLY); assertEquals(NOVEMBER_17,result); Date result=scheduler.scheduleMeeting(november(15), 5, 7); assertEquals(november(17),result); 125
    126. Учебный Центр Luxoft www.luxoft.ru/edu Шаблоны красной полосы Одношаговый тест Начальный тест Тест – объяснение Тест для изучения Еще один тест Регрессионный тест Одношаговый тест (one-step-test) one- step- test) Какой следующий тест выбрать из списка? списка? > который чему-либо научит вас > который вы сможете реализовать Не делайте большие шаги Если нет тестов, которые можно реализовать за 1 тестов, шаг – добавьте новые Вы всегда должны быть в 1 шаге от зеленой полосы Начальный тест (starter test) С какого теста начать разработку? разработку? > начните с варианта операции, которая не делает осмысленных действий, т.е. не делает ничего При написании реалистичного теста нужно ответить на слишком много вопросов: вопросов: > Где должна располагаться операция > Какие входные данные – корректны > Каков должен быть результат Один тест – один ответ 126
    127. Учебный Центр Luxoft www.luxoft.ru/edu Тест – объяснение (explanation test) Как распространить в команде использование автоматического тестирования? тестирования? > объясняя что-то, давайте тесты. Спрашивайте тесты у тех, кто пытается вам что-то объяснить Тест для изучения (learning test) test) Когда необходимо писать тесты для программного обеспечения, разработанного сторонними обеспечения, разработчиками? разработчиками? > перед тем как вы впервые воспользуетесь новыми возможностями Вы пишите тест, который позволяет вам убедиться тест, что API работает так как вы ожидаете Вы можете производить тестирование на совместимость с новыми версиями API Еще один тест (another test) Как предотвратить уход мысли от основной темы? темы? > добавьте постороннюю, интересную мысль в список тестов и вернитесь к основной теме 127
    128. Учебный Центр Luxoft www.luxoft.ru/edu Регрессионный тест (regression test) test) Что сделать в первую очередь, если был очередь, обнаружен дефект? дефект? > напишите самый маленький возможный тест, который его воспроизводит > сделайте его зеленым Подумайте о том как бы вы могли узнать о необходимости написать такой тест до обнаружения дефекта Шаблоны тестирования Дочерний тест Поддельный объект Самошунтирование Логи Тестирование обработки ошибок Сломанный тест Чистый комит Дочерний тест (child test) Как заставить работать тест, который получился тест, слишком большим? большим? > напишите тест меньшего размера, который представляет собой сломанную часть большого теста > сделайте его зеленым > заново напишите большой тест Вы сделали слишком большой шаг > почему? > как надо было сделать, чтобы тест получился меньше? 128
    129. Учебный Центр Luxoft www.luxoft.ru/edu Поддельный объект (mock object) object) Как выполнять тестирование объекта, который объекта, базируется на тяжеловесном внешнем ресурсе? ресурсе? Используйте mock-объекты для эмуляции mock- поведение ресурса > Возможно потребует рефакторинга кода, чтобы была возможность подменить ресурс на mock- объект Mock объекты быстрее Лучше читаются в тесте Позволяют сделать изолированный тест Самошунтирование (self shunt) Как убедится, что один объект корректно убедится, взаимодействует с другим? другим? > заставьте тестируемый объект взаимодействовать с вашим тестом > возможно придется применить “извлечение интерфейса” Альтернатива: используйте mock-объекты Альтернатива: mock- Строка-лог (log string) Строка- Как убедится, что обращение к методам убедится, просиходит в правильном порядке > создайте строку-лог в классе > каждый раз при обращении к методу, добавляйте к ней запись Альтернатива: используйте mock-объекты Альтернатива: mock- 129
    130. Учебный Центр Luxoft www.luxoft.ru/edu Тестирование обработки ошибок (crush test dummy) Как протестировать работу кода, который кода, обрабатывает ошибку, возникновение которой ошибку, маловероятно? маловероятно? > создайте специальный mock объект, который вместо реально работы генерирует ошибку Сломанный тест(broken test) тест( Как завершить сеанс программирования, если вы программирования, работаете в одиночку? одиночку? > оставьте последний тест неработающим Когда вы возвращаетесь к работе сразу видите где вы закончили, и вспоминаете о чем думали закончили, Чистый комит Как следует завершить сеанс программирования, программирования, если вы программируете в команде? команде? > все ваши тесты должны работать перед коммитом Это гарантирует что система находится в каждый момент в стабильном состоянии 130
    131. Учебный Центр Luxoft www.luxoft.ru/edu Шаблоны зеленой полосы Подделка Триангуляция Очевидная реализация От одного ко многим Подделка (Fake It) Если у вас есть сломанный тест, какой должна тест, быть самая первая реализация? реализация? > сделайте чтобы метод возвращал константу > постепенно трансформируйте константу в выражение с использованием переменных Зачем писать “ненужный” код? ненужный” код? > Лучше хоть какой-то работающий код, чем вообще не работающий > Психология – вы имеете зеленую полосу > Контроль на объемом работы (программистам свойственно пере-проектирование) Триангуляция (triangulate) triangulate) Как сформировать абстракцию с помощью тестов? тестов? > добавьте несколько тестов с “подделкой” > попробуйте обобщить код public void testSum() throws Exception public void testSum() throws Exception { { assertEquals(4,plus(3,1) ); assertEquals(4,plus(3,1) ); } //one more assertEquals(7,plus(3,4) ); public int plus(int a,int b) } { return 4; public int plus(int a,int b) } { return a + b; } 131
    132. Учебный Центр Luxoft www.luxoft.ru/edu Очевидная реализация (obvious implementation) implementation) Какой способ подходит для реализации простых операций? операций? > просто реализуйте их Подделка и Триангуляция позволяют вам двигаться маленькими шажками > но иногда вы абсолютно уверены как реализовать функционал Следите как часто вы сталкиваетесь с красной полосой От одного ко многим (one to many) many) Как реализовать операцию, которая работает с операцию, коллекцией объектов? объектов? > реализуйте ее для одного объекта > затем модифицируйте для работы с коллекцией таких объектов Шаблоны xUnit Утверждения Фикстура Тестовый метод Тест исключения Все тесты 132
    133. Учебный Центр Luxoft www.luxoft.ru/edu Утверждения (assertion) Как убедится что тест работает корректно? корректно? > Используйте утверждения public void testValidateQueue() throws Exception { Result res= processor.validateQueue(); assertTrue(res.isValid()); assertEquals(10,res.getStatusCode()); } Фикстура Как создаются и освобождаются общие объекты и ресурсы, используемые в тестах? ресурсы, тестах? > конвертируйте локальные переменные в члены класса теста > используйте фикстуру для инициализации и освобождения Фикстура (2) private ByteCodeVirefier mockVerifier; private Processor processor; public void setUp() { mockVerifier = newMock(...); processor = new Processor(mockVerifier); } public void tearDown() { resetMocks(); } public void testValidateQueue() throws Exception { Result res = processor.validateQueue(); assertTrue(res.isValid()); assertEquals(10, res.getStatusCode()); } 133
    134. Учебный Центр Luxoft www.luxoft.ru/edu Тестовый метод Тест – метод, имя которого содержит название метод, тестируемого метода с префиксов test Обычно располагается в том же package/namespace что и тестируемый класс public void testValidateQueue() throws Exception { Result res= processor.validateQueue(); assertTrue(res.isValid()); assertEquals(10,res.getStatusCode()); } Тест исключения (exception test) Тестируйте исключительные ситуации > Перехватываете исключение, игнорируете его > Делаете сбой теста в нормальном потоке исполнения public void testQueueFull() throws Exception { try { processor.validateQueue(); fail("QueueFullException should be thrown"); } catch(QueueFullException e) {} } Все тесты (all tests) Как запустить все (группы) тестов сразу? группы) сразу? > используйте тестовые наборы class IntegrationTests { public static Test suite() { TestSuite res = new TestSuite("Integration"); res.addTestSuite(SystemATest.class); res.addTestSuite(SystemBTest.class); res.addTestSuite(SystemCTest.class); return res; } } 134
    135. Учебный Центр Luxoft www.luxoft.ru/edu Когда писать тесты? тесты? При добавлении новой функциональности При изменении существующей функциональности При исправлении ошибки Чтобы разобраться в неизвестном коде Перед рефакторингом Насколько большим должны быть шаги? шаги? Какой объем функциональности должен покрывать каждый тест? тест? > TDD поощряет маленькие шажки > но не следует доходить до маразма и писать тест на каждую элементарную строчку Как много требуется промежуточных стадий для рефакторинга? рефакторинга? > зависит от инструментов > чем сложнее рефакторинг, тем больше шагов Сколько должно быть тестов? тестов? Обычно тестового кода больше чем рабочего В TDD прагматичный взгляд на тесты > цель – работающий код, в корректности которого мы в достаточной степени уверены > не пишите тест, если знание реализации позволяет быть уверенным в корректности Оценивайте среднее время между сбоями > Если система должна работать без сбоев до 100 лет, возможно требуется уделить внимание тестирование самых маловероятных ситуаций 135
    136. Учебный Центр Luxoft www.luxoft.ru/edu Когда удалять тесты? тесты? Чем больше тестов, тем лучше тестов, Если 2 теста кажутся одинаковыми, то одинаковыми, > не удаляйте тест, если это снижает вашу уверенность в корректности поведения системы > коммуникация. если другие члены команды рассматривают два теста как разные сценарии, то не удаляйте тест. Литература Кент Бек, “Экстремальное программирование. Бек, программирование. Разработка через тестирование.” тестирование. http://www.testdriven.com http://www.mockobjects.com/ Вопросы? Вопросы? Q&A 136
    137. Учебный Центр Luxoft www.luxoft.ru/edu Практикум TDD Workshop Разработка приложения по Use-case диаграмме с Use- использованием практик TDD Две итерации и изменяющимися требованиями Парная разработка Шаблоны TDD 137
    SlideShare Zeitgeist 2009

    + Евгений КривошеевЕвгений Кривошеев Nominate

    custom

    136 views, 0 favs, 0 embeds more stats

    Теоретический блок по модул more

    More info about this document

    © All Rights Reserved

    Go to text version

    • Total Views 136
      • 136 on SlideShare
      • 0 from embeds
    • Comments 0
    • Favorites 0
    • Downloads 5
    Most viewed embeds

    more

    All embeds

    less

    Flagged as inappropriate Flag as inappropriate
    Flag as inappropriate

    Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

    Cancel
    File a copyright complaint
    Having problems? Go to our helpdesk?

    Categories

    Tags