Почему тебе не нужно мутационное тестирование

Почему тебе не нужно мутационное тестирование

Информации по мутационному тестированию в сети довольно мало. Этому есть простое, на мой взгляд объяснение - в большинстве проектов поле автоматизации тестирования настолько непахано, что сколько бы людей там не работало, всегда будет чем заняться.

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

Теперь давай вернёмся к нашему коду и тестам. Чтобы понять, хороши ли наши тесты, нужно выяснить, сломаются ли наши тесты, если изменить поведение системы. Заниматься этим вручную никто не будет по ряду причин, в основном из-за того, что потребуется много времени - сначала выяснить, как должна вести себя система в определённых условиях, затем придумать, как поменять это поведение, внести изменения и выполнить тесты. Совсем другое дело, когда всё это можно сделать автоматически. Как и у большинства автоматических инструментов, результат будет не настолько хорош, как при ручном анализе, но в нашем случае сойдёт =).

Инструменты для мутационного тестирования работают примерно по одной и той же схеме:

  • генерируется изменение (мутация) в критически важном месте программы. Типичные мутации - инвертирование условия, изменение граничных условий цикла, замена математических операций и т.п.
  • запускаются все тесты (или часть из них, в зависимости от того, насколько умный инструмент)
  • анализируются результаты. Если хотя бы один тест упал или сломался - мутант считается убитым, если все тесты зелёные - выжившим

К моему удивлению, инструментов для мутационного тестирования мало. Изначально я хотел провести эксперимент на NodeJS проекте, и нашёл только 1(!) решение, что, в общем-то неслыханно в javascript-мире :). Этим тулом оказался Stryker, но и он в итоге не заработал, т.к. не поддерживает используемый в проекте ES6-синтакс. В итоге я взял другой проект, написаный на старой-доброй Java и запустил мутационное тестирование с использованием фреймворка Pitest.

Ну и теперь самое интересное - результаты и мои выводы:

  1. Большинство мутантов выжило в непокрытом коде. Поэтому даже не думай о мутационном тестировании если у тебя нет приличного покрытия. Что такое приличное покрытие - сложно сказать в общем случае, давай для простоты считать что это больше 80% для покрытия строк и близко к 100% для покрытия условий.

  2. Величина покрытия не отражает качество тестов. Были как полностью покрытые классы, в которых мутанты выживали, так и не полностью покрытые, в которых мутанты не выживали. Но это единичные случаи, которые можно считать исключениями из п. 1.

  3. Время выполнения нелинейно растёт с размером проекта. С ростом кодовой базы растёт как число возможных мутаций, так и число тестов, которые нужно выполнить для каждой мутации. В моём случае (проект среднего размера), юнит-тесты проходили за 10 секунд, а мутационное тестирование заняло около 10 минут.

Обдумывая всё это, я пришёл к интересному выводу: невозможно получить высокое покрытие, имея плохие тесты. Их поддержка будет отнимать слишком много времени, и они либо будут переписаны, либо выброшены. А значит, на проектах с высоким покрытием нет особого смысла запускать мутационное тестирование. Как нет смысла запускать его и на проектах с низким покрытием. Получается, мутационное тестирование никому не нужно? Возможно поэтому нет ни информации, ни разнообразия тулов в этой области?

comments powered by Disqus