*** ВНИМАНИЕ: Блог переехал на другой адрес - demin.ws ***

среда, 28 апреля 2010 г.

Programming with GUTs


Вчера был на тренинге Kevlin’a Henny под названием «Programming with GUTs». (Кевлин автор нескольких книг, например «Things Every Programming Should Know»)

«GUT» – это сокращение от Good Unit Tests.

Также я узнал выражения «Programming with BUTs» (Brittle Unit Tests) и «Programming with NUTs» (No Unit Tests).

Итак, «...with GUTS» - это хорошо, «...with BUTs» - хуже, «...with NUTs» - безумие.

Вся эта терминология построена на игре слов guts, butt и nuts в английском языке, которые своим двойным смыслом примерно отражают ситуацию в проекте, когда тестирование находится на указанном уровне (Good, Brittle, No).

Дядька очень интересно ведет беседу, нескучно. Вовремя переходит от приколов к важным выводам, что, например, что ошибки – это не зло, а данность (процесс создания программа сложен, и ошибки – это часть процесса), поэтому очень важно уметь использоваться их для своих целей. Глупо исправлять баг, не написав для него unit- или regression- тест, так как без теста информация, которую несет в себе баг-репорт, будет потеряна по исправления ошибки.

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

Например, дежурный вопрос менеджера (не очень умного менеджера) проекта: «А если мы будем практиковать TDD в разработке, это замедлит процесс?». И ваш правильный и политический верный ответ будет решительное «ДА!». «А правда, что написание тестов вместе с кодом призвано затормозить работу разработчика?». «ДА!!!».

В короткой перспективе – да, но в долгосрочной – нет, а совсем наоборот.

Есть хорошая аналогия. Можно ли доехать из места А в место Б на машине, у которой нет традиционных тормозов (а например, только ручник)? Теоретически да. Очень очень медленно, правильно используя торможение двигателем и ручником. Задача будет решена, но очень не быстро, и вероятность облажаться очень высока. А вот если машину оборудовать нормальными тормозами (~unit-тестами), цель которых останавливать машину (и увеличивать время достижения цели), то они также позволят активно разгоняться между торможениями, и как результат – приехать быстрее.

Мораль – тесты позволяют взять ваш код под контроль, превратить его из телеги, несущейся с горы на огромной скорости, в гоночный болид, который ездит также быстро, как и останавливается. Тесты радикально затормозят вас, когда это нужно. Но с другой стороны они позволят не дрожать всем телом, когда надо потрогать какой-то очень важный кусок кода (например, библиотеку строчек), а спокойно провести рефакторинг и проверить результаты.

Все эти посылы просто TDD очевидны и однозначно разумны, но я был поражен до глубины души, когда кто-то таки спросил из зала, а если я, мол, просто буду писать много комментариев, чтобы всем было понятно, что код делает. И это был не вопрос-прикол. Кто-то спросил это на полном серьезе.

По поводу комментариев была приведена также интересная аналогия. Комментарии в коде – это золото. Но золото обесценивается, когда его становится слишком много. Комментариев много бывает (особенно бесполезных).

На лабах было интересное задание. Программисту дается задание написать класс и unit-тесты для него. Затем эти тесты уже без кода класса даются другому программисту, и его просят воспроизвести функционал класса, основываясь только на тестах к нему. Результаты порой «радуют» даже продвинутых бойцов TDD.

Вывод как всегда простой: тестируйте как можно раньше, тестируйте чаще, тестируйте автоматизировано. Тестирование – это часть работы программиста в первую очередь, а тестера – во вторую.

P.S. Небольшое задание для интересующихся. Допустим, вы написали собственный алгоритм сортировки. Как его тестировать? Каковы будут критерии проверки, что результат такой, какой вы ожидаете?

8 комментариев:

  1. Я бы протестировал простые случаи - массив из 0, 1, 2, 3 элементов и, например, из 20. В дальнейшем, при выявлении багов, я бы добавлял юнит-тесты, позволяющие эти баги находить.

    ОтветитьУдалить
  2. А критерий проверки - все элементы поочерёдно сравнить с вручную отсортированным эталоном - все должны совпасть.

    ОтветитьУдалить
  3. а я бы помимо "массив из 0, 1, 2, 3 элементов и, например, из 20" еще бы сгенерил штук 5-10 случайных последовательностей на неск тысяч элементов, отсортировал, а потом сравнил с std::sort

    ОтветитьУдалить
  4. А что, если нет эталонного алгоритма? Например, вы написали хитрую сортировку, которая ориентирована на работу с большими файловыми потоками (например, взяв за основу MergeSort). То есть тот же std::sort не получиться использовать, так как нельзя взять с память все данные сразу. То есть вопрос в том: как тестировать алгоритм, свойства которого новые, и нет возможности с чем-либо сравнить. Вопрос, возможно, не имеет однозначного ответа.

    ОтветитьУдалить
  5. > Например, вы написали хитрую сортировку, которая ориентирована на работу с большими файловыми потоками...
    Ну тестить на небольших файловых потоках, которые можно сортирнуть с помощью std::sort.
    А в общем случае для абсолютно нового алгоритма - да, вопрос

    ОтветитьУдалить
  6. > То есть тот же std::sort не получиться использовать, так как нельзя взять с память все данные сразу.

    В случае сортировки - всё просто, мы ведь знаем, что она должна сделать с массивом. Просто проверим за линейное время условие того, что каждый последующий элемент отсортированной последовательности больше предыдущего.
    Можно протестировать свойства алгоритма, например насколько он соответствует теоретической сложности - удвоить размер размер сортируемого массива и посмотреть насколько увеличится время. Точно так же можно оценить потребление памяти.
    Действительно, процесс проверки результата выполнения некоторых алгоритмов сравним по сложности с самим алгоритмом, так что проще взять уже отлаженный алгоритм, решающий подобную задачу. Если такого нет, то, по-моему, не остается ничего, кроме как проверять удовлетворяет ли полученный результат тем свойствам, которые должен обеспечивать алгоритм, но тут уж лучше заняться формальной верификацией алгоритма\программы.

    ОтветитьУдалить
  7. Gunner Kade:

    Пусть на входе было: 5 4 3 2 1, а на выходе 6 7 8 9 10. В выходной последовательности каждый последующий элемент больше предыдущего. Как быть?

    ОтветитьУдалить
  8. Александр:
    Действительно, не подумал о таком баге.
    Можно запускать сортировку на предварительно перемешанных нами последовательностях, исходный отсортированный вид которых мы точно знаем. В качестве последовательности берем значения некоторой неубывающей функции в точках 1,2,...,n, так что мы всегда знаем какое значение должно стоять в конкретной позиции в отсортированной последовательности.

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