Программисты о тестировании

Программисты о тестировании

27.12.2018

Какие бывают тесты?

  1. Модульные или unit-тесты. Маленькие, быстрые, тестируют изолированные методы или изолированные классы.

  2. Приёмочные или функциональные. Мы решили, что это одно и то же. Приёмочный (acceptance) тест используют для приёмки пользовательской истории. Функциональные (functional) — проверяют реализуемость функциональных требований. Эти тесты средний по размерам и скорости, тестируют отдельные компоненты системы.

  3. Интеграционные. Большие, медленные, тестируют систему целиком в окружении, близком к боевому.

Тесты не гарантируют отсутствия ошибок

Если тесты не гарантируют отсутствие ошибок, для чего они нужны? Ответ нам даёт теория вероятности.

Предположим, что у нас есть работающая система без тестов и пользователи время от времени находят в ней ошибки. Мы собираем статистику, и она показывает, что 30% модулей содержали ошибки. Это значит, что вероятность события A «модуль не имеет ошибок» составляет 0,7. Обозначим её P(A).

Затем мы покрываем систему тестами и продолжаем её развивать. Тесты время от времени ломаются, но это случается не очень часто, при каждом пятом запуске, то есть в 20% случаев. Тогда вероятность события B «тесты успешно проходят» будет равна 0,8. Обозначим её P(B).

Какова условная вероятность P(A|B), то есть вероятность безошибочности модуля при условии, что тесты прошли успешно?

Для вычисления такой вероятности математики исользуют формулу Байеса: P(A|B) = P(B|A) × P(A)/P(B).

Важной частью формулы является условная вероятность P(B|A), то есть вероятность, что тесты проходят успешно, если в модуле нет ошибок. Она почти равна 1, потому что встречаются ситуации неправильных тестов, когда программа написана без ошибок, но тесты падают. Положим, что на практике она близка к 0,95.

Подставляем наши значения в формулу Байеса и получаем, что P(A|B) = 0,85. Наша уверенность в безошибочности модуля увеличилась с 70% до 85%, что довольно много. Мы никогда не сможем достигнуть вероятности 100% с помощью тестов. Но мы можем существенно снизить вероятность ошибок.

Почему не надо писать тесты?

Мы решили записать, почему тесты писать не надо. Крайние позицию позволяют обострить противоречия и действительно обнаружить граничные условия, где тесты становятся бесполезными, или наоборот, необходимыми.

  1. Программисты тратят время, а работодатель тратит зарплатные деньги.
  2. Тесты не гарантируют отсутствия ошибок.
  3. Тесты неинтересно писать.
  4. Тесты повышают стоимость сопровождения. При переписывании кода нужно переписывать и тесты.
  5. Если стоимость ошибки невелика, то тесты повышают стоимость разработки.
  6. Если программа простая, тесты не нужны.
  7. Если программа разовая, тесты не нужны.
  8. Сложно научиться писать тесты. Нужен наставник или надо несколько лет опыта.

А почему тесты надо писать?

  1. Нужны при рефакторинге.
  2. Если стоимость ошибки велика, то тесты снижают стоимость разработки.
  3. В больших программах тесты экономят время.
  4. Тесты повышают качество кода.
  5. Тесты являются дополнительным средством документирования. Правда, нужна хоршая IDE.

Как быть с legacy-кодом без тестов?

Legacy это система, которая написана двести лет назад людьми, который уволились сто лет назад. Работать она работает, переписывать её долго и дорого, и поэтому никто не переписывает.

Время от времени в ней надо править ошибки и даже добавлять новые функции. Иногда по-русски говорят «унаследованный код».

Можно ли внедрить модульные тесты в унаследованный код? Можно, но это тоже будет долго и дорого. Тесты требуют определённого подхода со стороны разработчиков, классы не должны быть слишком закрыты.

Поэтому для внедрения тестов код всё равно придётся рефакторить, а рефакторинг хорошо делать тогда, когда есть тесты, а тестов нет. Замкнутый круг.

Наше коллективное мнение: в унаследованном коде тестов нет, и не надо. Но если вы пишите новый код, обязательно пишите модульные тесты!

Когда писать, до или после?

TDD требует, чтобы тесты писались до кода. Все присутсвующие об этом знали. Но когда мы провели голосование, кому удаётся так писать на постоянной основе, выяснилось, что никому. Этот подход очень трудно сделать привычным, как сказал Алексей Пирогов, он контринтуитивен.

Поэтому, если не можете писать тесты до кода, пишите небольшими итерациями: написали модуль, тут же написали тесты.

В исследовательских проектах можно писать тесты после кода, потому что писать до не получится. Вообще непоятно, как оно должно быть. Писать тесты надо только тогда, когда становится понятно.

Кто должен писать тесты?

  1. Можно сделать написание тестов частью code review. Тогда Вася пишет код, а Петя тесты к этому коду.
  2. Можно следовать практике парного программирования. Вася пишет тест, Петя пишет код, чтобы тест проходил. Потом Петя пишет тест и передаёт ход Васе.
  3. Можно писать тесты самому. В большинстве случаев это самый предпочтительный вариант.

Black Box vs White Box?

Должны ли тесты знать о тестируемом коде? Если они не знают о коде, то код для них то чёрный ящик (Black Box): интерфейс понятен, а устройство непонятно.

Если они знают о коде, то код для них прозрачный ящик. Но мы в программировании не говорим «прозрачный ящик», у нас по традиции этот ящик белый (White Box), хотя это и не совсем точно передаёт смысл.

Наш ответ: модульные тесты находятся очень близко к коду, для них код это белый ящик. Приёмочные и интеграционные тесты наоборот, не должны знать ничего о коде.

Надо ли стопроцентное покрытие кода?

Общее мнение — нет, не надо. Действуем по принципу Парето, ищем критичный код, и покрываем его тестами. 80% покрытия выглядит разумным показателем.