Пользователь
0,0
рейтинг
24 сентября 2012 в 10:48

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

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

image

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

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

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

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

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

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

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

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

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

Подробнее
Реклама

Самое читаемое Разработка

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

  • +3
    На мой взгляд модульные тесты надо писать просто потому, что это не так уж сложно, а тестировать свой модуль вы будете так или иначе. Так почему бы не оставить тесты как наследие?

    Разрабатывайте свой модуль через тестирование (ну или почти так) и вы не будете париться о затраченном времени, так как оно и так было бы затрачено на какой-нибудь тестовый файл. При рефакторинге же вы как раз сэкономите время, так как будете знать где именно надо править код, чтобы программа вновь работала.

    Вы пишите, что программисты не тщательно тестируют код — это как раз проблемы программистов, тестировщики могут быть такими же (ибо почему нет?). Однако, это не отменяет необходимость приемочных тестов. Просто тестировать по идее надо каждый пук всё. Я глубоко убежден, что только так можно построить действительно хорошее приложение.
    • +2
      Скажем так — не надо писать приемочные тесты вместо модульных — надо вместе.
    • 0
      К сожалению есть несколько проблем

      >>Вы пишите, что программисты не тщательно тестируют код — это как раз проблемы программистов, тестировщики могут быть такими же (ибо почему нет?).

      Абсолютно разная мотивация — я уже писал почему.

      А во вторых, далеко не всё что пишеться, действительно нужно. Особенно в стартапах/прототипах. И очень часто вместо рефакторинга часть кода просто переписываеться с нуля. Приемочные тесты частично остаются, а вот юниты — под снос.

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

        Тестировщики тоже в первую очередь должны тестировать нормальное поведение программы, чтобы пользователи смогли ей пользоваться. И только потом тестировать — «а что будет, если я впихну очень длинную строку в input поле». Программист же должен заботиться о тестах на пустое значение и на заполненное в нормальной форме (если это не что-то вроде регулярного значения).
        • 0
          «никогда не угадаешь, что может ввести пользователь, которому дали поля ввода и клавиатуру» — не помню чьё, но таки правда. У тестеров есть деление на приоритеты ошибок, так что редко случаемые варианты использования идут более низким приоритетом.

          Насчёт мотивации — я видел огромное колличество влюблённых в свою работу разработчиков, которые готовы сидеть сутками (мой рекорд -26 часов кодинга) лишь бы продукт вышел в срок. Но хороший тестер найдёт ошибки в их, покрытых юнит тестами кодах. не потому что мотивации нет, просто она другая.
          • 0
            Мы не роботы — все ошибаются. Я уверен, что даже самый лучший тестер тоже может пропустить баг. И что теперь?
            • 0
              Вероятность меньше, вот и всё. Это как разница между военным и гражданским ПО — везде есть ошибки, но очевидно что в военной технике их меньше (потому что подходы к тестированию другие).
              • +1
                Кто Вам сказал?
                • 0
                  Просто опыт. Как правило тестер находит больше ошибок чем программист (особенно программист в собственно написанном функционале)
                  • 0
                    Тестировать надо всё. И надо ровняться на одних из лучших, а там тестируют всё и все.
                    • 0
                      да-да-да! :)
              • 0
                Где-то я читал, по статистике у NASA 1 ошибка на 1 млн. строк кода. И соглашусь с sectus — кто вам сказал, что вероятность ошибки тестера меньше?
                • 0
                  К сожалению, не смог быстро найти какими методологиями пользуются в NASA. Но почему то я считаю, что тестировщиков они используют.
                  • +1
                    А я и не говорю вам, что они не нужны. Я говорю о том, что не надо снижать значимость модульных тестов.
                    • +1
                      Я и не снижаю. Как правильно заметил DmitryKoterov юнит тесты это инструмент. И их значимость зависит от того, как вы их используете. Для меня Netbeans весьма значим, а мой товарищ кодит в Notepad++ и говорит что лучше ничего нет ;)

                      Я лишь хочу сказать, что модульное тестирование не гарантирует работоспособность продукта
                      • 0
                        Ну, если на то пошло — то ничего не гарантирует вам работоспособность продукта на 100% и на один тестер в здравом уме на даст вам эту гарантию. Так как навряд ли вы тестируете — как, например, поведут себя демоны вашего приложения, если при питание сервера просто выключить, а потом включить. Кроме того я вам скажу, что навряд ли много разработчиков в принципе заботятся о реализации функционала восстановления после падения системы, а надо бы.
                        • 0
                          >>Так как навряд ли вы тестируете — как, например, поведут себя демоны вашего приложения, если при питание сервера просто выключить, а потом включить

                          тестируем. Специально даже кнопки делали, на тестовом окружении — вырубить сервер, вырубить СУБД, вырубить кеш.

                          Но это мелочи. Попытаюсь ещё раз донести мысль — с тестерами багов проходит меньше (чем без них :)).
                          • 0
                            Ну по-моему вы не это в статье писали. Это все равно что говорить с руками человеку лучше, чем без них, с модульными тестами лучше, чем без них.
      • 0
        Кстати, можно ли сказать, что Вы несогласны с основным тезисом статьи Тестирование — это не поиск ошибок!?
        • 0
          С заголовком — нет, не согласен :) Тестирование — это поиск ошибок в зависимости от их приоритета и вероятности появления — а именно это хотел рассказать автор в статье по ссылке, и с этим я согласен.
          • +1
            Тут я не согласен. Тестирование — это не поиск ошибки. Тестирование — это способ удостоверится, что всё работает так как задумал… автор, разработчик, писатель ТЗ, заказчик и т.д…

            И, если я загляну в душу тестировщика так же легко как Вы заглянули в душу программиста, то увижу, что тестировщик, которому поставлена задача искать ошибки, будет искать ошибки, а не разбираться со сложными алгоритмами, которые были заложены в приложение.
            • 0
              Абсолютно согласен.

              Иногда, особенно после тренингов, тестировщики увлекаются поиском каверзных значений при которых не работает функционал в ущерб проверки success сценариев. Радуются как дети если нашли что. А при этом основной функционал не работает по итогам.

              Не согласен с выводом что юнит тесты не обязательны. И видимо другие виды что пишет разработчик. Предположу что автор не сталкивался с большими и сложными проектами где очень много кода и система не находится в состоянии покоя — не сделал и сдал заказчику а постоянно эволюционирует. Вот где изменения без тестов рушат все и сразу.
          • +1
            Как можно говорить о поиске ошибок в случае TDD/BDD? Ещё кода нет, чтобы ошибки в нём искать.

            Автоматическое тестирование — это, прежде всего, фиксация поведения, декларативное описание того, что код должен делать.
  • +2
    Юнит-тусты (модульные тесты) — инструмент ведения разработки в стиле TDD или полу-TDD (такой же, как текстовый редактор, клавиатура и т.д.). Без них в ряде случаев написать код если и не невозможно, то на несколько порядков сложнее, чем с ними (яму можно выкопать и инструментом-ложкой, но удобнее применять инструмент-лопату). Также это инструмент рефакторинга.

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

    Я бы еще выделил дымовые тесты (назовем их так, хотя я не вполне уверен), что-то промежуточное между 1 и 2. Они достаточно низкоуровневые для того, чтобы их мог писать только разработчик, однако основная их цель — снизить паранойю. Типичный пример — регрессионные тесты phpt. Крайне удобны тем, что их можно штамповать десятками в течение часа и тем, что они очень читабельны; неудобны же тем, что если они ломаются, нужен цикл отладки, чтобы понять, из-за чего же. Идеально подходят для библиотек, стабильность которых и гарантия неповторяемости прошлых исправленных багов очень важны.
    • 0
      если рассматривать инструменты (* тесты, автоматизацию, кодогенерацию), методики разработки (*DD, стратегии тестирования, рефакторинг) и цели (фиксацию требований, снижение доли ручного труда, выявление регрессий) в отдельности, то появляется больше степеней свободы.
  • +1
    Мне кажется что ваша проблема в том, что вы тестируете без спецификации, что вообще говоря, делать бессмысленно.

    Если программист может вот так вот взять и поменять checkInt() на проверку чисел со знаком когда до этого она проверяла только беззнаковые и его это ни капельки не смутило и не нужно было менять спецификацию (да, пусть это будет одно предложение в документационном комментарии перед функцией) — то нужно исправлять программистов, а не занижать значимость юнит-тестов.
    • +1
      К сожалению это реалии — Я ещё НИ РАЗУ не видел проект, где ВСЕ функции покрыты спецификациями/документацией. Максимум — короткий комментарий. Спецификации могут существовать на момент написания кода, но к моменту рефакторинга бывают напрочь утеряны.
      • +1
        Короткого комментария со словом «unsigned» было бы достаточно. А ещё лучше вынести это слово в название функции.
        • +1
          безусловно, checkUnsignedInt32 значительно лучше и понятнее. Но это лишь пример. Обычно, к сожалению, функции намного сложнее, и выписать что они должны делать в название доходчиво — не получается. Опять таки, на моей практике было как минимум 5 случаев, когда комментарий/название функции не соответствовали тому, что она делала(нет, не я их так назвал :) ).
          • 0
            Если функция сложнее и из названия не очевидно что она делает — значит должен быть документационный комментарий. Иначе как ей пользоваться?

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

    Эээ? Какая же функциональность будет повреждена? Те числа, которые ранее проходили по тесту, будут продолжать проходить. Если не было проверки на то, что отрицательные числа не должны проходить, значит, это не было важно, а значит, функциональность не затронута.

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

    Тоже не верно, потому что первое правило при изменении — напиши новый юнит-тест, который сейчас не проходит, а после изменения будет проходить. Вот он и должен будет отвалиться при регрессии.
    • 0
      >> Если не было проверки на то, что отрицательные числа не должны проходить, значит, это не было важно, а значит, функциональность не затронута.

      А было ли это не важно? Чтоб ответить на этот вопрос нужно либо искать автора, либо разбераться где функция используется (все места).

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

      Верно, именно этот юнит-тест матерящийся автор и поменяет обратно, после исправления функции назад :) А ваш функционал всё равно отвалится. Нет, конечно, в идиальном мире, всё будет хорошо.
      • +3
        А было ли это не важно?

        Не было теста — не было важно.

        Верно, именно этот юнит-тест матерящийся автор и поменяет обратно, после исправления функции назад

        А вот так делать банально нельзя. Если в тесте написано, что «при отрицательных значения должно возвращать true», то он не просто так написан. Дальше нужно поднимать историю кода и смотреть, зачем так написано.

        И я вас уверяю, вид тестов (модульные/интеграционные/функциональные) тут не при чем, эта проблема совершенно одинакова в любом из них.
  • +1
    Как правило, это разработчик, и его цель — покрытие кода юнит-тестами. А это значит, что скорее всего, юнит-тест будет весьма не полным

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

    В общем, культура TDD — это не написание тестов для галочки, а написание всевозможных сценариев для проверки. И автор забыл упомянуть такой подход: нашли баг — напиши юнит тест, чтобы этот баг не всплыл обратно. После этого юнит тесты заиграют новыми красками.
    • 0
      >>Как правило, юнит тесты вообще не пишутся.
      • 0
        Блин, привычка из скайпа, нажимать ctrl+enter чтоб перейти на следующую строку
        По теме:
        Да, как правило юнит тестов вообще нет. И применяеться не TDD, а покрытие кода после написания. И именно о нём я писал (я же не написал сравнение подхода TDD с функциональными тестами). И в этой ситуации модульные тесты часто пишуться для галочки — есть покрытие — есть, ну и фиг с ним. И это ужасно. не потому что программист плохой, или хочет сделать бажный код, что Вы, НЕТ! Просто он воспринимает модульное тестирование как новую блаж начальства.
        • +2
          > модульное тестирование как новую блаж начальства

          Разве тесты в этом виноваты? Или, я начинаю догадываться, цель статьи — выяснить какие тесты более полезны при условии что тесты делаются для галочки?

          • 0
            Попробуйте прийти в коллектив, который НЕ покрывал продукт тестами, и попытаться ввести в разработку модульные или приёмочные тесты.

            Я думаю после такой практики, Вы со мной согласитесь. Или напишите ещё одну статью, которую я с огромным удовольствием прочитаю
            • +1
              Зачем писать статью если есть замечательная книга — Working effectively with legacy code. Хочу обратить ваше внимание на определение понятия legacy code, которое там даётся.
              • 0
                спасибо, прочитаю
            • 0
              Ну вот мы ввели модульные и интеграционные тесты существенно позже десяти тысяч строк кода. В системе, где до этого никаких тестов не было. И что?
              • 0
                И этим мы зафиксировали текущее поведение системы. Правильное, неправильное — всё вместе.
                • +1
                  Ну вообще нет, потому что «ввели» не значит «покрыли все целиком». Но идея была в том, чтобы сначала зафиксировать легаси-поведение (не важно, какое, важно, что остальная система работает именно так), а потом его рефакторить. Собственно, как у Физерса и полагается.

                  Я к тому, что ничего невозможного.
                  • 0
                    > Ну вообще нет, потому что «ввели» не значит «покрыли все целиком».

                    Если в этом смысле — тогда согласен.
              • +1
                >>Это первая ошибка. Нельзя покрывать код тестами после написания (не важно, какими).

                Вы сами себе противоречите, или я Вас просто не понимаю?
                • +1
                  Вы меня не понимаете.

                  Покрытие уже написанного кода тестами «задним числом» — это вынужденная мера, реализация которой в разы (если не на порядки) дороже, чем «нормальный» порядок.

                  Именно поэтому «нельзя» так делать. Но иногда приходится.
                  • 0
                    Согласен. Но ведь лучше всё таки покрыть, чем оставить «голым» код, правда? :)
                    • 0
                      Лучше покрыть. Дальше читаем WEwLC.
        • +2
          И применяеться не TDD, а покрытие кода после написания

          Это первая ошибка. Нельзя покрывать код тестами после написания (не важно, какими).

          Просто он воспринимает модульное тестирование как новую блаж начальства.

          Это вторая ошибка.

          И обе ошибки — в методологии.
          • 0
            Когда вы приходите на частично готовый проект, то нужно работать, а не рассуждать какая методология была верной. И переписать с нуля проект вам никто не даст. Я говорю именно о таких условиях.
            • +1
              Вот имено это и приводит к проектам, которые остаются в состоянии «частично готовый» навсегда. Никто не мешает внедрять юнит-тесты в середине проекта, просто нужно уметь это делать.

              Собственно, то, что о чем вы пишете — это совершенно академический пример, прекрасно описанный в книжке xUnit Test Patterns. Вы прошли ровно по всем граблям: пытаетесь применить модульное тестирование в середине проекта, пытаетесь это сделать для уже написанного кода и при этом не имеете опыта работы с модульными тестами. Собственно, результат тоже описан в книге — недовольство юнит-тестами как подходом и отказ от них.
              • 0
                Можно один вопрос: Почему Вы считаете, что я отказываюсь от модульных тестов?
                • 0
                  Вот из этой фразы: «Юнит-тесты — желательно, но зависит от настроения разработчиков». Ну и весь пассаж про «недостатки» модульных тестов.

                  • 0
                    Всё в этом мире имеет недостатки, неправда ли?

                    Я полностью согласен с фразой «тестировать всё что только можно». Но если у вас очень ограничены ресурсы, зачастую приходиться выбирать. Вся идея статьи лишь в том, что юнит тесты не заменят приёмочных, и если при комите изменний полагаться только на результаты юнит-тестов то велик шанс что-нибудь угробить.
                    • 0
                      А приемочные тесты, по вашему, могут заменить модульные?
                      • 0
                        Если все приёмочные тесты после ваших изменений выполняются удачно, то — добавляйте свои изменения в репозиторий смело, если что — вините тестеров :)

                        А вот ошибку без модульных тестов искать будет много тяжелее, это да
                        • +1
                          Ага.

                          А вы гоняете все приемочные тесты по всей системе после каждого изменения?
                          • 0
                            Лично я стараюсь прогнать после стягивания кода из репозитория (собственно это быстрейший способ убедиться что ничего никто не сломал). И перед тем как сам буду заливать значительные изменения.
                            • 0
                              Гхм…
                              Простите, не могли бы вы нам рассказать про размер системы и количество тестов?
                            • 0
                              По всей системе? И сколько часов это у вас занимает?

                              (ну и да, я что-то не понимаю, вы тестировщик или разработчик? если тестировщик, то почему вы «стягиваете» код? если разработчик, то почему вы гоняете функциональные тесты, которые в вашей системе ценностей пишет тестировщик?)
                              • 0
                                Разработчик. И мне не сложно попросить тестера прогнать приёмочные тесты для модуля (так уж пошло, что мой модуль довольно изолирован, и вносимый мной код повредить чужой не может, а вот наоборот — всегда пожалуста :) )

                                Честно говоря, моя б воля, ставил бы на ночь и всю прогонку, но из-за особенности организации работы, далеко не все приёмочные тесты проходят удачно (попросту — на них забивают, и «радуются» на релизах)

                                Прогонка всех тестов для модуля занимает минут 15.

                                Отвечая на коментарий выше, нет размер системы и количество тестов указать не могу :)
                                • 0
                                  И мне не сложно попросить тестера прогнать приёмочные тесты для модуля

                                  То есть — не для всей системы. Это означает, что тот факт, что остальная система не упала, вы ничем гарантировать не можете (только утверждением, что вносимый вами код ничего не ломает, но кто же разработчику верит?).

                                  из-за особенности организации работы, далеко не все приёмочные тесты проходят удачно

                                  И вы после этого говорите, что приемочные тесты чем-то лучше модульных?
                                  • 0
                                    безусловно. Именно поэтому. Именно потому что работоспособность небольшого количества приёмочных тестов способно гарантировать работоспособность моего модуля. Как результат — ни единого фейла на релизах.

                                    Я пишу исходя из реальных условий. Далеко не идиальных. И если б я мог я их изменил. Но в данных условиях, единственная надежда на приёмочные тесты.
                                    • 0
                                      Именно потому что работоспособность небольшого количества приёмочных тестов способно гарантировать работоспособность моего модуля.

                                      Раз уж все сравнения идут только на примере вашего модуля, получается, что именно вы, а не абстрактный разработчик, не можете так написать юнит-тесты, чтобы гарантировать работоспособность вашего модуля?

                                      единственная надежда на приёмочные тесты.

                                      На те самые, на которые забивают.
                                      • 0
                                        Раз уж все сравнения идут только на примере вашего модуля, получается, что именно вы, а не абстрактный разработчик, не можете так написать юнит-тесты, чтобы гарантировать работоспособность вашего модуля?


                                        Юнит тесты тестируют работу модуля, а не взаимодействие системы, верно? А кому какое дело, что мой модуль должен работать верно, если он не работает в конкретном окружении? :)

                                        На те самые, на которые забивают.

                                        Вы либо не внимательно читаете, либо стараетесь всеми силами доказать свою правоту. И в том и другом случае дискуссию нет смысла продолжать
                                        • 0
                                          А кому какое дело, что мой модуль должен работать верно, если он не работает в конкретном окружении? :)

                                          Если модуль работает верно, а ошибка в окружении — ее кто правит?
                                          • 0
                                            Зависит от обстоятельств. Иногда проще поправить самому, чем искать виноватого(Или ждать от него фиксов). Иногда (когда мы говорим о очень важных участках кода) лучше найти автора.

                                            А как у вас?
                                            • +1
                                              Если «поправить самому», то вы уже вылезли за пределы своего модуля. А это возвращает нас к вопросу «кто тестируют всю систему на регрессию и как часто». А если «лучше найти автора», то приемочные тесты на ваш код продолжают не работать, и пинать ответственность можно бесконечно.

                                              А как у вас?

                                              А у нас shared code ownership, но в границах личной компетенции.
                                              • 0
                                                А это возвращает нас к вопросу «кто тестируют всю систему на регрессию и как часто».

                                                Если не можешь изменить мир — измени себя. Если невозможно, по обьективным причинам обеспечить работоспособность всей системы, я всё же могу обеспечить работоспособность подответственного участка работы.
                                                • 0
                                                  Проблема в том, что обобщения о том, какие тесты выгоднее, сделанные на основе одного модуля, на систему распространяются плохо.

                                                  Особенно в вопросе, что выгоднее — покрыть некий (широко используемый) кусочек функциональности юнит-тестом или функциональными тестами.
                                                  • 0
                                                    маленький и широко используемый — ТОЛЬКО юнит тест (т.к. он будет документацией, как можно использовать, а как — нет) :) А вот использование этого маленького кусочка в рамках вашего модуля — уже лучше функциональным. А как Вы считаете?
                                                    • 0
                                                      маленький и широко используемый — ТОЛЬКО юнит тест

                                                      Правда? Тогда перечитайте свой собственный пост, начиная с абзаца «Самое главное — даже успешное прохождение всех юнит-тестов не гарантирует правильной работы продукта: ведь одна и та же функция может быть использована в различных частях системы, в то время как юнит-тест писался для неё с оглядкой лишь на один вариант использования.»

                                                      Теперь уже вы себе противоречите, потому что ваша checkInt — типичный пример маленького широко используемого куска.
                                                      • 0
                                                        Нет, не противоречие. повсеместно используемые библиотеки должны быть покрыты юнит-тестами, но это не гарантирует их правильное применение. А вот правильное применение, и вообще правильность и работоспособность написанного функционала гарантируют приёмочные тесты. Что я и написал в комментарии.
                                                        • +1
                                                          А откуда возьмутся полные юнит-тесты на повсеместно используемую библиотеку, если в вашем примере checkInt ими не была покрыта?
                                                          • 0
                                                            Очевидно, что полное покрытие — это из той же области что и код без багов. И я о полном покрытии не говорил
                                                            • 0
                                                              Я, знаете ли, уже запутался.

                                                              Давайте на конкретном примере. Вот есть тупой валидационный метод checkInt, который проверяет, что переданная в него строка — число, а не какая-нибуть НГХ. Этот метод используется во всем проекте.

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

                                                                  Вы, простите, затраты на это считали?
                                                                  • 0
                                                                    Ваш вариант?
                                                                    • +2
                                                                      При добавлении каждого места использования (для унаследованных систем — при обнаружении бага или доработки) пишется два юнит-теста. Первый — на то, что потребитель вызывает метод и реагирует на его результат заданным образом (тест на встраивание). Второй — что метод корректно себя ведет при том использовании, которое нужно этому потребителю. Поскольку тесты из второй группы очень быстро начнут повторяться, скоро их не надо будет писать.
                                                                      • 0
                                                                        Первый — это скорее интеграционный или даже функциональный тест будет, причём в случае булевых функций типа checkInt их нужно писать минимум два, а в более сложных случаях ещё больше.
                                                                        • 0
                                                                          Вообще, нет. Это будет чистый юнит-тест с behaviour verification. И пишется их столько, сколько классов возвращаемых результатов у зависимости.
            • 0
              Кстати, вы сами-то кто по жизни? Руководитель, разработчик, тестер, аналитик, подчеркнуть нужное?
              Вы выдаете очень категоричные суждения, слабо подкрепленные аргументацией, и мне хотелось бы знать, чьи суждения я читаю.

              Спасибо.
  • +3
    «К тому же, существует огромное число функций, проверить которые модульным тестированием невозможно, либо очень сложно (это функции, поведение которых зависит от состояния системы в целом). „

    You do it wrong.
    Такие функции очень сильно воняют, и в первую очередь — кандидаты на рефакторинг.
    У вас проблема с декомпозицией.
    Модульное тестирование — оно потому и модульное, что тестирует изолированный модуль, а не состояние всей системы.
  • 0
    Плохая задача для тестеров: «найти ошибку». Хорошая — протестировать систему, т.е. предоставить наиболее полную информацию о системе. В которой указаны и ошибки. Извините, но мне как тестеру глаза сильно режет.
  • +1
    Имхо, писать и модульные, и функциональные тесты обязанность разработчика. Для тестировшиков есть приемочные, нагрузочные и т. п. И уж точно модульные и функциональные пишутся не для выявления ошибок, а для закрепления спецификаций (пускай и выраженных только самими тестами). Если они пишутся на существующий код, то должны сначала пройти, даже если фиксируют явно бажное поведение, и лишь потом изменяться для правильного поведения.

    А противостояние модульных и функциональных нелепо. Они решают разные задачи, но комбинируя их можно уменьшить число и/или сложность и тех, и других.
  • +1
    >Функциональные тесты — обязательно
    >Юнит-тесты — желательно, но зависит от настроения разработчиков

    Я бы в зависимость от прям вот настроения не ставил. Если ждать вдохновения — то никаких юнит тестов не будет.
    И тогда легко и быстро скатиться к ситуации, когда
    — 30% времени билд тупо не собирается или собирается, но не проходит базовые проверки
    — 30% времени тестировщики тратят время на описание «простых и очевидных» багов в трекере, продираются через эти баги, обходят их как-то
    — 10% времени тестировщики объясняют, почему тестирование движется так медленно
    — 5% времени уходит на перепланирование в связи с поплывшими сроками
    — 25% времени тратится на тестирование (включая сюда анализ, тест-дизайн, настройку окружения, репорты и т.д.)

    Утрировано, но, думаю, идея понятна. Если программист снимает с себя ответственность за качество (проверять — это ж работа тестировщика!), то никакой отдел тестирования уже ничего не спасет. Разве что тестировщики начнут чинить процессы разработки и внедрять полезные практики, вместо того чтоб грызть кактус…
    • 0
      Если все приёмочные тесты проходят удачно, то чего не хватает тестерам чтоб работать дальше?
      • 0
        Приемочные тесты — во-первых, это ну совсем базовые вещи (по кр. мере у нас так). Это никак не обещает, что при шаге в сторону все продолжит работать. Всегда приходится искать баланс между «больше проверок» и «быстрее». Достаточно скоро мы попадаем в ситуацию, когда билды собираются много чаще, чем успевает отработать приемка. И тогда начинаются вопросы: а зачем вы используете такой старый билд?
        Кроме того — приемочные тесты это регрессия, а тестировать мы стремимся новые фичи, нет? Они не покрыты приемочными тестами по определению, и запросто могут не работать. Далее по тексту — баги, обходные маневры, время потраченное в багтрекер, тестровщик глядящий на дождь за окном.
        • 0
          хм, мы пошли другим путём. По сути все действия тестера записываються в приёмки. Которые мы можем для новых фич запускать отдельно (ну согласитесь, тяжело добавив поле сломать чтонибудь на другой странице). Более того, некоторые тесты пишутся даже с опережением функционала (да, частично вслепую). Таким образом, новые фичи покрываются автоматическими тестами очень быстро (буквально за пару часов после завершения работы над ними), после чего программист начинает править баги, до тех пор, покуда приёмочные тесты не будут выполнены.
          • 0
            звучит здорово, серьезно
            подумаю, как бы нам к такому приблизиться
        • +2
          Кроме того — приемочные тесты это регрессия, а тестировать мы стремимся новые фичи, нет? Они не покрыты приемочными тестами по определению

          Вообще-то, в обратном порядке. Приемочный тест-кейс на фичу должен быть до ее появления (в идеале — включен в требования). Регрессионным он становится тогда, когда мы начинаем повторять эти тесты по уже принятым фичам (чтобы проверить, что они не сломались, т.е. мы не нарушили старые требования).
          • 0
            Упс. Я имел в виду приемочный тест билда, не фичи. Недопонял сперва, извиняюсь.
            Согласен, приемочный тест на фичу сильно упрощает жизнь.
  • 0
    Я думаю мысль автора была в несколько другом. Важность модульного тестирования в некоторых методиках разработки очевидно. Однако его почему-то часто воспринимают как панацею, забывая о других вариантах тестирования. И тут я автора поддерживаю — сколько бы ни было у вас Unit-тестов и какое бы ни было покрытие кода, функционального тестирования это не заменит.
  • +1
    Функциональные тесты полностью определяют (по крайней мере должны) работоспособность продукта. И прежде всего нужны заказчику/руководителю разработки. Юнит тестирование прежде всего нужно самим разработчикам, для быстрого нахождения ошибок или проверки последствий рефакторинга. Поэтому приоритет должен стоять таким образом:

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

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

    мне кажется, что ваша приоритезация, вероятно, отражает текущую фазу разработки. когда, в моменте, доминирует проектирование «сверху-вниз», то появляются acceptance-тесты и тесты, порождаемые по дедукции в tdd, как на картинке. когда синтезируется решение «снизу-вверх» — юнит и интеграционные.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.