Юнит тесты Vs функциональные тесты — взгляд руководителя и разработчика

в 6:48, , рубрики: functional testing, unit-testing, тестирование, метки: , ,

Современную разработку программного обеспечения тяжело представить без автоматического тестирования — по сути это единственный способ защитить продукт от разрушительных изменений (то есть изменений, повреждающих существующий функционал).

image

Обычно используют два вида автоматических тестов:
Модульное тестирование (тестирование отдельных частей продукта, обычно отдельных функций/методов)
Функциональное тестирование — тестирование некого функционала продукта, при этом продукт воспринимается как единый «чёрный ящик».

Но давайте зададим интересный вопрос — действительно ли нужны оба вида тестирования сразу, и если нет — то какое из них важнее?

Итак, для начала модульное тестирование.
Кто пишет? Как правило автор модуля/метода/функции, т. к. обычно только он знает, что данная функция ДОЛЖНА делать. Как правило, это разработчик, и его цель — покрытие кода юнит-тестами. А это значит, что скорее всего, юнит-тест будет весьма не полным (как правило разработчики проверяют лишь работу с корректными данными, либо добавляют небольшой набор некорректных данных, т. к. у них не стоит задачи «сломать» свой код), либо, если разработчик действительно ответственно подойдёт к процессу написания юнит-теста, то разработка займёт вдвое больше времени. Не верите? А ведь только качественное покрытие функции «является ли аргумент числом» требует более 10 проверок. К тому же, существует огромное число функций, проверить которые модульным тестированием невозможно, либо очень сложно (это функции, поведение которых зависит от состояния системы в целом).
И даже правильная работа всех модулей системы, отнюдь не гарантирует их правильное взаимодействие.
Ещё один момент, если у вы хотите ввести модульное тестирование в продукт, на котором оно раньше не использовалось — то это очень серьёзные трудо-затраты, и покрытие небольшого числа функций абсолютно ничего не даёт.

Самое главное — даже успешное прохождение всех юнит-тестов не гарантирует правильной работы продукта: ведь одна и та же функция может быть использована в различных частях системы, в то время как юнит-тест писался для неё с оглядкой лишь на один вариант использования.
Простой пример: допустим у нас есть функция checkInt(a){return /^[0-9]+$/.test(a)}. При этом в юнит-тесте нет проверки на отрицательное число. Что это значит? А то, что если некий разработчик модифицирует данную функцию как
checkInt(a){return /^-?[0-9]+$/.test(a)}, то юнит-тест будет проходить как ни в чём не бывало. А вот функционал будет повреждён. А после того, как ошибка будет найдена и исправлена (функция вернётся к своему изначальному состоянию), отвалится часть функционала в другом месте. При неизменно удачном прохождении юнит-тестов.

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

Однако, следует признать, что модульное тестирование великолепный инструмент, если вы пишите некую библиотеку, предназначенную для использования многими разработчиками — в этом случае модульные тесты будут выполнять и роль документации к функциям (ведь не секрет, что мы часто пропускаем описание функции и сразу смотрим примеры использования). Впрочем, в данном случае, сама библиотека является продуктом, и модульные тесты не отличимы от функциональных.

Теперь вернёмся к функциональным тестам. Как правило, они пишутся тестерами. У которых стоит задача найти ошибку (по крайней мере своим тестерам мы всегда ставим такую задачу). А значит будет больше проверок на нестандартные данные — что согласитесь, просто великолепно! К тому же, мы можем разделить правами доступов код продукта и тестов, что позволит избежать изменений тестов «чтоб он был зелёный, потому что надо релизиться»

К тому же, функциональными тестами гораздо проще покрывать готовый продукт, чем модульными — т. к. гораздо проще понять что конкретно должна и не должна делать определённая часть пользовательского интерфейса, чем определить что ДОЛЖНА делать данная функция. И самая большая прелесть — вы можете начать покрывать функциональными тестами только самые важные части продукта — и они будут исправно гарантировать их работоспособность.

Подведём итог.
Функциональные тесты полностью определяют (по крайней мере должны) работоспособность продукта. И прежде всего нужны заказчику/руководителю разработки. Юнит тестирование прежде всего нужно самим разработчикам, для быстрого нахождения ошибок или проверки последствий рефакторинга. Поэтому приоритет должен стоять таким образом:

  • Функциональные тесты — обязательно
  • Юнит-тесты — желательно, но зависит от настроения разработчиков

Автор: vaevictus

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js