Unit-тестирование на энтузиазме.pptx
- Количество слайдов: 26
Unit-тестирование на энтузиазме Кожевников Дмитрий 19 марта 2014 года Devent - Красноярск
Докладывает • • Кожевников Дмитрий Олегович. Net разработчик Инженер-программист, ЗАО Красинформ Интересы: паттерны и практики разработки качественного кода, unit-тестирование и TDD, новые технологии в стеке. net, больше кода для бога кода • Email d. o. kozhevnikov@gmail. com
О чём я не буду говорить • Что такое unit-тесты и зачем они нужны? • Как сделать код в принципе пригодным для unitтестирования? • Что за принципы SOLID? • Как это - Inversion of Control, зачем Io. C-контейнер? • Программирование на основе интерфейсов? • TDD – WTF? !111
Проблематика доклада • Большая инерция кода при изменениях • Большие и нечитаемые unit-тесты • Тестируемость вместо простого дизайна
Корни зла • Большое число зависимостей тестируемых классов • Тестирование тяжёлых алгоритмов • Логика инициализации в конструкторе • Код формально слабосвязный • А тесты монструозны и нечитаемы
Решения нашей команды • Принципы ▫ Как Боб Мартин • Паттерны ▫ Прикладное велосипедостроение • Запахи ▫ Чем пахнет наш код • Метрики ▫ Как будто и так не видно
Принципы • Для тестов ▫ Весь тест в одном методе ▫ Стабы, моки на лету по полиморфным классам(у нас Moq) ▫ Контроль за объёмом тестовых данных • Для тестируемых классов ▫ ▫ ▫ Тестирование концепции, а не реализации Объектная декомпозиция вместо структурной Один вызов – одно правило Тестирование изолированных абстракций Каждый вызов оставляет след
Паттерны • • • Нормальная декомпозиция Шаг за шагом Список команд Отделение инициализации Контекст для вызовов
Нормальная декомпозиция Domain. Service Sub. Service 1 void Perform. Logic(); Result 1 Sub. Action(); public void Perform. Logic() { var r 1 = _service 1. Sub. Action(); var r 2 = _service 2. Sub. Action(r 1); var r 3 = _service 3. Sub. Action(r 2); } Sub. Service 1 Result 2 Sub. Action(Result 1 r); Sub. Service 1 Result 3 Sub. Action(Result 2 r);
Нормальная декомпозиция • Плюсы ▫ Простота и интуитивность решения ▫ Относительно простой рефакторинг • Минусы ▫ ▫ Увеличение числа зависимостей Расщепление алгоритмов по нескольким классам Число выделяемых классов ограничено Ценность абстракций выделяемых классов может быть сомнительна
Шаг за шагом Step. By. Step. Tests Step. By. Step Director Action(Dependency 1 d 1, Step 1(Dependency 1 d); Dependency 2 d 2, Step 2(Dependency 2 d, Result 1 r); Dependency 3 d 3) Step 3(Dependency 3 d, Result 2 r); Step 1 Test(); Step 2 Test(); Step 3 Test() Dependency 1 Dependency 2 Dependency 3 Sub. Action() var r 1 = _step. By. Step 1(d 1); var r 2 = _step. By. Step 2(d 2, r 1); var r 3 = _step. By. Step 3(d 3, r 2);
Шаг за шагом • Плюсы ▫ Сохранение логики внутри одного класса ▫ Небольшой прирост числа зависимостей ▫ Можно выделить много шагов • Минусы ▫ Сложное решение, сложно выделить шаги ▫ Страдает инкапсуляция логики класса ▫ Необходимо передавать промежуточные данные
Список команд var tkn = new Token(); foreach(var c in Commands) { a. Execute(tkn); } Command. Impl 1 Action(); Execute(Token t); Sort. Order { get; } Command. Director ICommand Token Command. Impl 2 Execute(Token t); Sort. Order { get; }
Список команд • Плюсы ▫ ▫ Можно выделить много шагов Детали реализации полностью скрыты Четкое разделение ответственности исходного класса Расширяемость • Минусы ▫ ▫ Сложное решение, нужен Io. C-контейнер Подходит для логики с независимыми шагами Нужно следить за порядковыми номерами Логика алгоритма разбросана
Выбор паттерна • Нормальная декомпозиция ▫ Простой случай, легко выделить службы ▫ Службы абстрактно целостны ▫ Есть вероятность смены реализации служб • Шаг за шагом ▫ Необходимо сохранить логику внутри класса. ▫ Нужно выделить много шагов, чтобы удобно тестировать ▫ Шаги не сильно связаны по промежуточным данным • Список команд ▫ Нужно выделить большого числа шагов ▫ Шаги независимы и слабо связаны по промежуточным данным ▫ Есть необходимость добавлять и удалять шаги
Отделение инициализации Init. Object. Test Init. Object(Dep 1 dep 1, Dep 2 dep 2); Initialize(); Action(); Ctor. Test(); Initialize. Test(); Action. Test(); void Ctor. Test() { . . . // Act var i = new Init. Object(); // Assert . . . } void Initialize. Test() { // Arrange var i = new Init. Object(); // Act i. Initialize(); // Assert . . . } void Action. Test() { // Arrange var i = new Init. Object(); . . . // Act i. Action(); // Assert. . . }
Отделение инициализации • Плюсы ▫ Разгрузка тестов от поддержки логики конструктора ▫ Возможна разгрузка конструктора от части зависимостей • Минусы ▫ Ограничение инкапсуляции состояния класса ▫ Дополнительный вызов метода инициализации
Контекст вызовов Work. Context Facade Actor void Act
Пример кода
Контекст вызовов • Плюсы ▫ Разгрузка конструктора от контекстных зависимостей ▫ Организация контекста для логики(DCI, а? ) • Минусы ▫ Таки вариант Service. Locator ▫ Чудной интерфейс
Запахи • • Много заглушек и глубокая настройка в Arrange Дублирование кода в Arrange Вспомогательные методы в фикстурах Защищённые конструкторы только для тестов Тестовые реализации вместо моков Зависимости только для конструктора Логика инициализации в конструкторах
Метрики • Метрики по переменным: ▫ ▫ ▫ Переменные примитивных типов, литералы, коллекции и т. д. Стабы созданные фреймворком изоляции Моки - классами в тестовой сборке Тестируемые объекты • Метрики по настройкам стабов/моков ▫ ▫ Настройки свойств Настройки методов Настройки сallback’ов Настройки выброса исключений
Итоги • • • Разделение ответственности – необходимость, а не искусство Код определяет качество тестов Слабосвязный дизайн != тестируемый дизайн Тестируемый дизайн != простой дизайн Тестируемый дизайн == простой рефакторинг
Вопросы • Каков порог входа (время входа) в практики unitтестирования? • Как достигнуть баланса между тестируемостью и простотой? • Оправдывают ли unit тесты усложнение дизайна? • Нужны ли паттерны тестируемого кода?
Ссылки • Антипаттерны TDD http: //habrahabr. ru/post/43761/ • Те же посылки, но совсем другие решения, цикл статей, Юрий Солдатенков http: //solyutor. blogspot. ru/2012/03/ock. html • Тестируемый дизайн vs хороший дизайн, Сергей Тепляков http: //sergeyteplyakov. blogspot. ru/2013/04/vs. html • Проект Mock. Metrics, R# плагин подсчёта метрик unit тестов https: //github. com/Kozhevnikov. Dmitry/Mock. Metrics/tree/trunk
Спасибо за внимание!