Привет, меня зовут Сергей, и я инженер автоматизации тестирования.

Не так давно (13 Сентября 2023) на Хабре опубликовали статью "Подборка выдающихся статей по тестированию". В приведенном списке есть и статья "Фетиш юнит-тестов" Мартина Сустрика.

Я считаю эту статью вредной, и постараюсь показать – чем именно. Кто-то из читателей согласен с утверждениями Мартина. Возможно, кто-то из них не смотрел с позиции QA на эти утверждения. Именно для них я изложил свою точку зрения.

Я не буду приводить обширных цитат из оригинальной статьи, поэтому рекомендую перед дальнейшим чтением ознакомиться с её содержанием. Это не займет много времени – она короткая.

1. Return on investment of unit tests is an order(s) of magnitude lower than that of end-to-end tests

Автор утверждает, что ROI unit-тестов значительно (на порядок) ниже, чем E2E. Тут кажется странным несколько вещей.

Если сравнивается окупаемость ручного и автоматизированного тестирования, то, разумеется, ручное тестирование, на короткой дистанции, имеет более высокий ROI. Но, когда количество необходимого времени на исполнение регрессионного набора достигает определенных значений, и ROI уверенно снижается - положение спасает именно автоматизация. А если говорится об автоматизированных E2E-тестах, то автор попросту вводит в заблуждение читателей. Автоматизация E2E-тестов является наиболее дорогой автоматизацией, которую весьма невыгодно внедрять на начальных этапах проекта.

Также, говорится о начале процесса QA, и при этом выносится за скобки методология, и стадия разработки. Если используется TDD, то unit-тесты становятся неотъемлемой частью проекта. Рекомендую ознакомиться с исследованием   "An Initial Investigation of Test Driven Development in Industry" Боби Джорджа и Лори Уильямса. В нём весьма убедительно показано, что инвестиции в TDD (соответственно, в unit-тесты) имеют большую окупаемость (если говорить о качестве), чем тестирование "черным ящиком". Помимо этого, если проект только начался, то о каком E2E-тестировании может идти речь?

Наиболее вредным в этом утверждении является то, что ответственность за проверку качества продукта полностью передаётся команде тестирования (им же выполнять E2E тестирование). Качественный продукт является плодом совместных усилий команды программистов, тестировщиков, аналитиков. И QA-инженеры призваны следить за тем, чтобы процессы верификации, валидации выполнялись всеми, и на всех этапах разработки продукта.

2. End-to-end tests test the critical path. Unit test do not

Автор утверждает, что E2E тесты проверяют критически-важные сценарии, а unit-тесты нет.

Это действительно так. Но без unit-тестов нужно несравнимо больше тестовых сценариев. А это значит, что требуемые ресурсы команды растут с каждым выпуском мажорной версии продукта. Необходимо написать тестовые сценарии, составить необходимые тестовые наборы, и исполнить их. А также тратить дополнительное время на поиск корневой причины провальных проверок. Поясню. При E2E, зачастую, неочевидно из-за дефекта какого модуля проверка провалилась. А хороший тестировщик при составлении баг-репорта должен указать конкретную область, которая имеет дефект. И чем точнее он это сделает, тем меньше времени нужно на исправление дефекта программистом.

Здесь же автор утверждает: "However, user wants the product to work in common cases in the first place.". Да, пользователи этого хотят. Но как же часто пользователи используют продукт "неожиданными" способами. Причин много: проблемы UX, отсутствие опыта работы с подобными приложениями. И, в конце концов, наличие богатой фантазии или неуёмного желания экспериментировать.

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

3. Unit tests ossify the internal architecture

Автор утверждает, что unit-тесты становятся бесполезными или вредными при рефакторинге архитектуры приложения.

Но, позвольте, как раз при рефакторинге unit-тесты могут оказать неоценимую помощь разработчику. Они показывают, как этот код работает! Без них программисты обречены на рефакторинг legacy-кода. А теперь ответьте: какая причина чаще убивает желание изменить архитектуру приложения - необходимость переписать unit-тесты или legacy-код?

Хорошие unit-тесты покрывают не просто методы или модули (unit'ы), а именно функциональные блоки (units of work). Соответственно, при изменении архитектуры, зачастую, достаточно заменить вызываемый и результирующий метод. Не утверждаю, что это просто, но точно проще, чем переписать всю логику теста.

Unit of work — это сумма всех действий, которые происходят между вызовом метода приложения, и единственным конечным результатом, который может наблюдать пользователь.

Вредным в этом утверждении является то, что автор находит оправдание плохой (закостенелой) архитектуре в наличии unit-тестов. Если архитектура имеет изъяны – исправьте это. А unit-тесты могут помочь вам. Red, Green, Refactor один из примеров такой помощи.

4. There are things that can't be unit-tested

С этим утверждением автора трудно спорить. Действительно, не всё может и должно быть покрыто unit-тестами. Но это же не является аргументом в пользу полного отказа от unit-тестирования, верно? Движемся дальше.

5. Some stuff has no rigorous acceptance criteria

Автор приводит в пример графический интерфейс пользователя, и говорит, что в нем нет строгих критерий. А значит тестировать весьма затруднительно. Но графический интерфейс состоит из компонентов (поля ввода, радиокнопки, чекбоксы и т.д.), которые имеют требования и логику поведения. При каких-то условиях они должны становится неитерабельными для пользователя или невидимыми. Они могут иметь валидационную логику с выводом сообщений. Эти требования могут быть проверены не только ручным тестированием, но и unit-тестами.

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

6. Unit tests are easy to measure and you should fear that

Автор предостерегает от использования такой метрики тестового покрытия как: количество тестов или количество строк покрытого ими кода. И не дает другие примеры метрик, которые работают лучше. Например: количество найденных дефектов с разбивкой по классам; количество дефектов, найденных за потраченное на тестирование время; среднее количество дефектов на один тест. С этими и другими метриками вы можете познакомиться в книге "Code Complete" Стива МакКоннелла (ISBN: 0735619670).

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

Заключение

Нужно отдать должное автору за то, что в заключении он просит задумываться о той работе, которую читатель проделывает. Прошу об этом и я.

Unit-тесты - это мощный инструмент, который может быть использован в разных ситуациях, и с разной успешностью. Часто важность таких тестов сложно переоценить. И на столько же часто они могут быть совсем бесполезными. Ищите правильные подходы. Оценивайте степень влияния пока ещё несуществующих на вашем проекте unit-тестов на качество продукта. Даже если вы имеете негативный опыт в прошлом.

Комментарии (16)


  1. MihaTeam
    22.09.2023 13:43

    Хотелось бы отметить один важный момент, достаточно часто в юнит-тестах используют моки. В этом нет ничего плохого, но есть моменты, которые из-за этого стоит учитывать.
    К примеру у нас есть 4 юнита - A, B, C, D
    в A используются: B, C
    в C используются: D
    В данном примере сделаем вид, что все юниту используют моки на месте других юнитов (обычно конечно не везде моки, и это хорошо, но для примера оставим так)
    И к примеру
    A, C, D - покрыты тестами, а B - нет
    В данном случае мы имеем, что т.к C и D покрыты тестами, то мы можем быть уверены в их корректной работе(хотя всегда уверенным на 100% быть не стоит), а вот в юните A мы уверены быть не можем, тк его внутренняя логика хоть и верная, но когда мы подключим вместо мока юнит B, то могут быть ошибки.
    При таком сценарии юнит тесты наоборот несут больше вреда, чем пользы, тк могут дать ложную уверенность в корректности кода(хотя на самом деле это касается всех видов тестирования, если они написаны плохо)


    1. Sklott
      22.09.2023 13:43

      Вот чесно не понял логики. Как могут хорошие юнит-тесты в этом случае быть вредными. У вас же нет тесов на модуль B, значит и уверенности в его корректности у вас быть не может. Такого рода ошибки отлавливаются интеграционными и E2E тестами, не важно автоматическими или ручными.


    1. bzzz1k Автор
      22.09.2023 13:43

      могут дать ложную уверенность в корректности кода

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

      Когда исполнятся интеграционные тесты (они ведь есть, правда?) - мы увидим дефект. Во время фикса, я надеюсь, разработчик покроет злосчастный "B" unit-тестами. Проблема решена.


  1. gmtd
    22.09.2023 13:43

    Нужно смотреть на контекст
    На фронтенде юнит-тесты действительно не на порядок, а на два хуже e2e тестов


    1. bzzz1k Автор
      22.09.2023 13:43

      Не уверен, что правильно понял, что значит "хуже". Не так эффективны?

      Не покрывайте тривиальный код unit-тестами. Такой код может быть как на back-end, так и на front-end. В современных приложениях его больше на front-end. Это всего лишь значит, что тут нужно меньше тестов.


      1. gmtd
        22.09.2023 13:43

        "Хуже" - это что в оригинальной статье сформулировано как ROI - return on investment

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

        e2e тестирование намного эффективней и полезней на фронте.

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


        1. bzzz1k Автор
          22.09.2023 13:43
          +1

          Повторюсь, смыслом unit-тесты наделяет сложность кода, который они покрывают. Если код тривиален, то его где угодно покрывать не имеет смысла.

          Если занимается бесполезным трудом, то, разумеется, ROI будет низким.


  1. nin-jin
    22.09.2023 13:43
    -1

    unit-тесты покрывают не просто методы или модули (unit'ы), а именно функциональные блоки (units of work)

    Вы зачем unit-of-work тесты называете unit-тестами? Это совершенно разные виды тестов.

    Если используется TDD, то unit-тесты становятся неотъемлемой частью проекта. Рекомендую ознакомиться с


    1. bzzz1k Автор
      22.09.2023 13:43
      +1

      Да это не особо я их так называю, а Рой Ошеров. Я всего лишь скромно придерживаюсь его точки зрения.

      Можно назвать такие unit-тесты компонентными. Можно назвать интеграционные тесты sociable-unit-тестами. Главное правильно выбирать подход к тестированию.

      Спасибо за видео - ознакомлюсь.


    1. bzzz1k Автор
      22.09.2023 13:43
      +1

      На всякий случай - я не адепт TDD. Я адепт адекватных подходов к каждому конкретному случаю. А теперь про видео.

      Спикер приводит в пример "парадокс воронов", и при этом на протяжении всего видео использует "подмену тезиса". Если честно, после первого случая хотелось закрыть видео, но пересилил это желание. И пожалел.

      Адепт TDD не редко думает, что падение теста на явно некорректном коде может хоть что-то сказать о том, будет ли тест падать на коде, который похож на корректный.


      Что тут спикер имел ввиду крайне сложно понять. Что-то типа: кто-то думает, что если тест упал на некорректном коде, то совсем не факт, что он не упадет на корректном? Тест отражает желаемое поведение модуля. Если тест упал на "корректном" коде, то каким образом выясняется "корректность"?

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

      Складывается впечатление, что спикер видит смысл TDD в написании "красных" тестов. Нужны не "красные" тесты, а тесты, которые показывают какое поведение модуля мы ожидаем.
      Какую конкретно ситуацию рассматривает спикер? При использовании TDD такая ситуация возможна при рефакторинге legacy-кода. Так и в чем проблема того, что тест сразу "зеленый"? В том что этот код работает так как мы ожидали? Понять решительно невозможно.
      И исходя из этого спикер делает вывод: "Для обеспечения качества, мы вынуждены явно нарушать основную идею TDD - сначала тест, потом код.". Феноменально.

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

      Да из чего "следовательно"?!
      Да из чего "следовательно"?!

      Почему минимальная реализация это некорректный код? Почему минимальная реализация не будет расширяться, а будет "отправляться в мусорную корзину"?

      Однако, всё ещё остаётся довольно высокая вероятность (того что) в любой момент столкнуться с такой ситуацией, что очередное изменение кода сломает все ваши тесты.

      Очередное изменение сломает тесты. Это точно про TDD? Может сначала тесты изменим, а после код править будем? Нет? Ну, ладно.

      Как было отмечено ранее, применение TDD в общем случае скорее вредно, чем полезно.

      Какой общий случай? Где отмечено-то? Спикер всё видео в неявном виде обсуждает ситуацию рефакторинга, а не разработки нового кода.

      Извините, моё мнение о видео можно кратко описать: кг/ам


      1. funca
        22.09.2023 13:43

        @nin-jin часто выступает против ветра. Однако, по моим наблюдениям, делает это абсолютно искренне. Кг, но как автор он хорош.


  1. funca
    22.09.2023 13:43
    +1

    Как померять ROI для юнит тестов? - вот это интересный вопрос.

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

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


    1. bzzz1k Автор
      22.09.2023 13:43
      +1

      Из очевидного - в сравнении. В исследовании, которое я упомянул в статье ("An Initial Investigation of Test Driven Development in Industry") сравнивают проекты выстроенные вокруг unit-тестов, с проектами на "ручной тяге", если правильно помню.

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


      1. funca
        22.09.2023 13:43

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

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

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


  1. Bobovor
    22.09.2023 13:43

    Я работаю в маленьких конторах и юнит тесты видал только в картинках и зачатках.

    Хочу задать вопрос, вот тут все любят науку и вообще "science bitch!!", а есть какие научные исследования о эффективности всего это? Не обязательно тестов, но тех же паттернов, методик и т.п.

    Я не говорю, что это херня, но кто нибудь проверял, объективно, с какого момента это херня, а с какого нет?


    1. bzzz1k Автор
      22.09.2023 13:43

      Что Вы, в итоге, имеете ввиду под "это"? Паттерны больше про ремесло, чем про науку. По методикам (самым разным) исследований полно. Возьмите любой ВУЗ с профильными предметами - есть диссертации, дипломные и всякие остальные квалификационные работы.