суббота, 15 февраля 2014 г.

Всеволод Леонов задал правильные вопросы о тестировании

http://programmingmindstream.blogspot.ru/2014/02/blog-post_5.html?showComment=1392387769479#c6160914394058860654

Цитирую:

как говорил герой одного романа "Немного найдётся людей, с которыми мне приятно общаться. Вы - один из них". 

Цитата:
"Если для использования кода вам надо "продираться" через ДЕБРИ сильносвязанных классов, которые "упрятаны где-то в кишки", то ТЕСТ - вы СКОРЕЕ ВСЕГО - НЕ НАПИШЕТЕ. И - "скорее всего" этот код - ТАК ЖЕ ТРУДНО использовать, как и ПИСАТЬ К НЕМУ ТЕСТ."

Т.е. условием тестируемости (факта возможности существования атомарных тестов) является спец-архитектура. Ну ладно, структура. Ииииии? Тесты уже не могут быть лошадь. Лошадью становится структура классов (на подмножестве шаблонов слабосвязного проектирования)? Более того, экспертиза головы Александра Валерьевича бо не написать "неправильный классы", которые не смогут есть "неправильный мёд" в виде тестов?

Вопрос без оскорблений (чтобы не вызвать взаимные). Получается, нужна "культура производства кода", да? И какова она?

Второй вопрос, вытекающий из первого. С одной стороны - атомарность тестов подразумевает "атомарность" классово/методового построения. Но ведь ТЗ в некотором смысле "интегрально". По крайней мере точно не доходит до атомарно-тестируемых методов изолированных классов.

В общем-то получается, что "без Люлина пока никуда"?
В инженерной практике я один раз в жизни допустил ошибку (в раннем студенчестве). Нарисовал конструкцию "планетарного редуктора". Чертеж был реальным, но "неразбирабельным". Т.е. шестерни стояли на месте, но вынуть их было нельзя. Соответственно, изготовить и собрать тоже. Пришлось корпус "распиливать" пополам и делать сборную на винтах конструкцию.
С классами/методами/иерархией также? Не пора ли уже поговорить про "слабую связность"? (после ответов на 2 предыдущих вопроса, если захотите :))

Конец цитаты.

Ну во-первых хочу сказать СПАСИБО Всеволоду.

"как говорил герой одного романа "Немного найдётся людей, с которыми мне приятно общаться. Вы - один из них". "

На самом деле со мной - ТРУДНО общаться :-) но это - "лирика" :-)

И ещё - "без Люлина" - ещё КУДА :-)

Теперь к вопросам.

Забегу немного вперёд:

"Получается, нужна "культура производства кода", да?"

Да! "Культура производства" - НУЖНА.

Без неё - НИКУДА.

И я в своём блоге именно о "культуре производства" и пишу.

Ваша "культура" может не совпадать с "моей", но "какая-то культура" - ДОЛЖНА БЫТЬ!

Какая? Это уже вопрос обсуждений.

Теперь по сути.

"Т.е. условием тестируемости (факта возможности существования атомарных тестов) является спец-архитектура. Ну ладно, структура. Ииииии? Тесты уже не могут быть лошадь. Лошадью становится структура классов (на подмножестве шаблонов слабосвязного проектирования)? Более того, экспертиза головы Александра Валерьевича бо не написать "неправильный классы", которые не смогут есть "неправильный мёд" в виде тестов?"

Значит так.

Тесты это НЕ лошадь и НЕ ТЕЛЕГА.

Мы про это уже говорили вот тут - http://habrahabr.ru/post/206828/

Лошадь это - ТЗ
Телега это - КОД
Тесты это - ОГЛОБЛИ, связывающие телегу и лошадь (код и ТЗ).

Для начала скажу вот что - ЗАБУДЬТЕ о ПРАВИЛЬНОЙ АРХИТЕКТУРЕ. Жёстко да?

Но это - ПРАВДА.

ПРАВИЛЬНУЮ АРХИТЕКТУРУ без тестов и ТЗ построить - НЕВОЗМОЖНО.

Повторю - ПРАВИЛЬНУЮ АРХИТЕКТУРУ без тестов и ТЗ построить - НЕВОЗМОЖНО.

При этом - ТЗ оно - ПЕРВИЧНО.

Теперь посмотрим на САМУЮ плохую ситуацию - "ни тестов, ни ТЗ - нет, есть ОДИН ЛИШЬ голый код".

Что делать в этой ситуации?

ГЛАВНОЕ - не паниковать :-)

Если вы уже ОСОЗНАЛИ, что вам нужны тесты - вы прошли ПОЛ-ДОРОГИ.

Если же НЕ ОСОЗНАЛИ, то у вам "и так всё ПОКА ХОРОШО". Дальнейший текст не для вас. Спите спокойно :-)

Итак.

ТЗ нету, тестов нету. Есть ГОЛЫЙ КОД.

Что делать для того, чтобы писать тесты?

Забудьте об АРХИТЕКТУРЕ и "теории".

Попробуйте написать ПЕРВЫЙ тест и НАСТРОИТЬ ИНФРАСТРУКТУРУ тестирования.

С чего начать применительно к Delphi?

DUnit - вот наше ГЛАВНОЕ СЛОВО.

Почитайте документацию к нему. Я не буду тут вас перегружать подробностями.

Далее что?

Далее ПРОЩЕ ВСЕГО "оттолкнуться" от какой-нибудь ОШИБКИ, которую вы СЕЙЧАС СОБРАЛИСЬ править.

Желательно НЕ ОЧЕНЬ СЛОЖНОЙ.

Повторю! ПРОЩЕ ВСЕГО "оттолкнуться" от какой-нибудь ОШИБКИ, которую вы СЕЙЧАС СОБРАЛИСЬ править.

Можно писать тесты и по ТЗ и по НОВОЙ функциональности, но ОТТАЛКИВАТЬСЯ от УЖЕ найденных ошибок - ПРОЩЕ ВСЕГО.

Если вы решили "жить по-новому" - сделайте вот что:

НЕ ПРАВЬТЕ сразу ошибку.

ПОПРОБУЙТЕ для НАЧАЛА написать к ней тест.

В ТЕРМИНАХ вашей системы и её ПРОЕКТНЫХ классов.

Просто создайте TestCase DUnit'а (как ИНТЕГРИРОВАТЬ DUnit в СУЩЕСТВУЮЩЕЕ приложение - я напишу ПОЗЖЕ, напомните мне об этом если я ВДРУГ ЗАБУДУ) и просто пишите КОД для воспроизведения ошибки.

Типа:

1. Открываем такую-то форму.
2. Берём из неё такой-то объект.
3. Дёргаем такие-то методы.
4. Получаем то-то и то-то.
5. Ещё дёргаем методы.
6. Получаем ошибку.

Если у вас ЭТО ПОЛУЧИЛОСЬ, то я ВАС ПОЗДРАВЛЯЮ.

Вы написали ПЕРВЫЙ ТЕСТ.

Что с ним дальше делать?

Ну во-первых прогнать раз "сто" и убедиться, что он приводит к ошибке.

После этого - СМЕЛО ПРАВЬТЕ свой код, который собирались править и В ОТСУТСТВИИ тестов.

Поправили? Нашли ошибку?

Код заработал?

Что ещё тут ВАЖНО отметить - править код можно лишь нажимая кнопку RunTest.

Без всяких "пасов руками".

Итак. Получилось поправить ошибку?

Тест прошёл?

Ошибка перестала повторяться у пользователей?

Если ДА, то я вас ОПЯТЬ ПОЗДРАВЛЯЮ, вы не только написали первый тест, но ещё и исправили ошибку с его помощью.

Далее поступайте по приведённому сценарию.

Только не забывайте вот что:

0. Берём НОВУЮ ошибку.
1. Сначала прогоняем СУЩЕСТВУЮЩИЕ тесты. Убеждаемся, что они ВСЕ прошли.
2. Потом пишем ОДИН НОВЫЙ тест.
3. УБЕЖДАЕМСЯ, что ОН НЕ ПРОХОДИТ.
4. Опять прогоняем, все СУЩЕСТВУЮЩИЕ тесты (включая ВНОВЬ НАПИСАННЫЙ).
5. Убеждаемся, что НЕ ПРОХОДИТ только ОДИН ВНОВЬ НАПИСАННЫЙ.
6. Если это не так, то проверяем "то мы сломали".
7. Когда это становится "так", то начинаем исправлять ошибку.
8. Исправили.
9. Убедились, что ВНОВЬ НАПИСАННЫЙ тест прошёл.
10. Прогоняем ВСЕ тесты, убеждаемся, что они ВСЕ прошли.
11. Если это НЕ ТАК, то разбираемся с ними.
12. Когда всё становится "так" - закрываем ошибку и коммитим код.
13. Вытягиваем всё из CVS/SVN.
14. Опять прогоняем ВСЕ тесты.
15. Разбираемся с непрошедшими.
16. Спим спокойно.

Теперь что делать, если ТЕСТ НАПИСАТЬ НЕ ПОЛУЧИЛОСЬ?

Тут есть несколько рекомендаций.

Самая простая вот какая:

Возьмите ДРУГУЮ "более простую ошибку" (про ТЗ и НОВУЮ функциональность я пока умолчу).

И попробуйте с ДРУГОЙ, но "более простой ошибкой" проделать всё описанное выше.

Получилось? Поздравляю!

Не получилось - берите ДРУГУЮ.

И так "пока вам не надоест".

В итоге - не получилось написать тест?

Пойдём тогда дальше.

Раз НЕ ПОЛУЧИЛОСЬ написать тест НИ К ОДНОЙ ошибке, то это означает (sic!), что АРХИТЕКТУРА "плохо тестируемая". Скорее всего она - МОНОЛИТНАЯ.

Что делать?

Вот ТУТ начнём "пилить архитектуру".

Выберите "самую простую" и "банальную" и хорошо повторяемую ошибку из тех, что пробовали ранее.

Повторю! ХОРОШО ПОВТОРЯЕМУЮ.

Она повторяется "руками"? Вы в этом УБЕДИЛИСЬ?

Если НЕТ, то возьмите - ДРУГУЮ.

Если ДА, то пойдём дальше.

Опять попробуем написать код для "воспроизведения ошибки".

Опять наверное не получится (раз не получилось ранее).

Если же "вдруг получилось" (что свидетельствует о том, что вы уже "набрались опыта" и подготовили ИНФРАСТРУКТУРУ), то идём к началу статьи и применяем "алгоритм" описанный выше.

Если НЕТ, то читаем дальше.

Тут давайте проанализируем - ПОЧЕМУ не получилось написать тест воспроизводящий ошибку (ТУТ ПОВТОРЮ - "руками" она ЖЕЛЕЗНО повторяется).

Тут возможны варианты:

1. Какие-то классы приватные.
2. Какие-то методы приватные.
3. Невозможно съэмулировать "реальные данные".
4. Недетерминированное поведение.
5. Что-то ещё наверняка забыл.

Тут в ПЕРВУЮ ОЧЕРЕДЬ выбирайте ДРУГУЮ ОШИБКУ и возвращайтесь с началу статьи.

Всё перебрали?

Тогда читаем дальше.

Опять, если УЖЕ ЕСТЬ тесты - прогоняем их и УБЕЖДАЕМСЯ, что они прошли.

Чтобы была уверенность, что СОСТОЯНИЕ ЗАФИКСИРОВАНО.

Далее "пилим архитектуру" так, чтобы было возможно написать НАШ ТЕСТ.

Время от времени НЕ ЗАБЫВАЕМ прогонять СУЩЕСТВУЮЩИЕ ТЕСТЫ.

Делаем так, чтобы из кода можно было вызвать "все ручки" для ПОВТОРЕНИЯ ОШИБКИ.

Применяем хелперы и хаки как например написано тут - http://programmingmindstream.blogspot.ru/2014/02/private.html

По ВОЗМОЖНОСТИ - страраемся НЕ СВЯЗЫВАТЬСЯ с GUI (про GUI это ОТДЕЛЬНАЯ тема, как я уже писал GUI тестируют от "бедности", но если НАДО - тему GUI - Отдельно могу развивать).

В итоге - ДЕЛАЕМ ТАК, чтобы наш проектный код позволял вызывать ВСЕ НУЖНЫЕ методы и классы.

(Тут надо ПОНИМАТЬ, что я говорю про РАЗРАБОТЧИКОВ, а не ОТК, про ОТК - опять же - ОТДЕЛЬНЫЙ разговор)

И ОПЯТЬ ЖЕ - время от времени НЕ ЗАБЫВАЕМ прогонять СУЩЕСТВУЮЩИЕ тесты.

Если же таковых нет - то хотя бы ОСНОВНЫЕ ПРЕЦЕДЕНТЫ системы "протыкиваем руками".

Пока тестов нет - "мы на это обречены".

Как только появится "два десятка" тестов - этого можно уже не делать.

И опять же - тут НЕ ДУМАЕМ об "атомарных" или "комплексных" тестах.

Рано ещё.

Итак.

Мы "пилим архитектуру" и стараемся написать тест.

(опять же.. про БД, mock'и, GUI и т.д. и т.п. - я пока умолчу... возьмите ТАКУЮ ОШИБКУ, чтобы ТАКИХ потребностей не возникало)

(не зря кстати Ник Ходжес говорил про "калькулятор" и "атомарные" тесты)

Итак. Рано или поздно мы "запилили архитектуру" и написали тест.

Если НЕ ПОЛУЧИЛОСЬ, то ОПЯТЬ берём ДРУГУЮ ОШИБКУ и возвращаемся к началу статьи и пытаемся писать тест к ней.

Если же ПОЛУЧИЛОСЬ, то..

Во первых я вас ПОЗДРАВЛЯЮ.

Вы опять НАПИСАЛИ ТЕСТ.

Но! КРОМЕ ЭТОГО - вы сделали архитектуру БОЛЕЕ ТЕСТИРУЕМОЙ.

Далее что делаем?

1. Убеждаемся, что тест повторяет ошибку.
2. Убеждаемся, что ДРУГИЕ тесты не "отъехали".
3. Если ДРУГИХ тестов НЕТ - "протыкиваем" руками. Насколько можем.
4. Исправляем ошибку.
5. Убеждаемся, что тест прошёл.
6. Проверяем ДРУГИЕ тесты ну или "опять протыкиваем руками".

Вот вам ВСЯ СХЕМА.

Итеративная.

Сначала (когда нету ДРУГИХ ТЕСТОВ) - сложно и "на грани фола".

Чем больше ДРУГИХ тестов, тем УВЕРЕННЕЕ она РАБОТАЕТ.

Ну это я на ПЕРВЫЙ вопрос ответил. На остальные - далее.

Или и на остальные ответил?

P.S. И ещё.. что касается АРХИТЕКТУРЫ - про "матрёшку" не стоит забывать - http://habrahabr.ru/post/188604/

Про то, что правится лишь "конкретный слой". Как луковая кожура.

P.P.S. СПАСИБО Всеволоду Леонову за ПРАВИЛЬНЫЕ вопросы.

2 комментария:

  1. Дочитал до (Цитата) ПРАВИЛЬНУЮ АРХИТЕКТУРУ без тестов и ТЗ построить - НЕВОЗМОЖНО."

    Пока остановился. Слишком сильно сказано.
    Теперь я начинаю понимать, почему до сих пор люди бьются над понятием "Архитектура ПО" и кто такой "Архитектор ПО". Пока я буду читать дальше... Давайте подумаем в свете сказанного Александром - "что такое архитектура ПО".

    Конечно, есть гигантский соблазн сослаться на википедию. Однако я твёрдо убеждён, пока ты не "откроешь это сам для себя на уровне формулировок", чужие цитаты останутся цитатами. Нужно "переоткрыть" понятие "архитектура ПО", причём это должен сделать каждый сам. Если, конечно, хочет в этом разбираться.

    Получаю огромное интеллектуально наслаждение от бесед с Александром. Как-то классикам я доверяю меньше, чем "живым современникам".

    Кстати, нужно ли вебинар Ника Ходжеса переводить на русский?

    ОтветитьУдалить
  2. Конечно !!!
    В принципе я всё понял, однако многие разработчики удалены от темы.

    ОтветитьУдалить