TDD не работает.


Правда? Странно. У меня всегда работал хорошо.


А вот новое исследование говорит об обратном.


Новое исследование?


Да, глубокое исследование, повторившее шаги другого исследования, проведенного несколько лет назад. Оба показали, что TDD не работает. Новое исследование проходило в нескольких местах, использовало слепой анализ. Выглядит убедительно.


Авторы считают его убедительным?


Авторы рекомендуют проводить новые исследования. Но они скорее всего просто скромничают. Результаты довольно убедительные.


Какие результаты?


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


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


Ну, тем не менее. DHH был прав. TDD is Dead.


Хмм. Хорошо, так что именно сделали авторы исследования? Как они пришли к такому выводу?


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


Как ты узнал про это исследование?


Я прочитал про него в блоге. В конце автор сказал, что оно заставило его переосмыслить TDD. Он считал, что TDD работал.


Что же, хорошо, давай посмотрим на это исследование. Хмм… Да, тут говорится, что они сравнивали TDD с TLD.


Что такое TLD?


Test LAST development. Это когда ты пишешь юнит-тесты после написания кода.


Ну вот! Исследование показало, что лучше писать тесты в самом конце!


Хмм. Нет, кажется, это совсем не то, что показало исследование. Вообще-то оно показало, что нет существенной разницы.


Ну хорошо. То есть если я пишу код, а потом пишу тесты, то это не хуже, чем TDD.


Ну, не совсем. Во всяком случае это не то, о чем говорит исследование. В его ходе сторонников TLD просили работать "небольшими кусками".


Небольшими кусками?


Да. Сторонники TLD писали немного продакшн-кода, а потом немного тестов.


Ага, понял. То есть они писали продакшн-код 10 минут, а потом 10 минут писали тесты, или что-то в этом роде.


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


Ага. ОК. Ну, мое утверждение все еще верно. Они писали продакшн-код, потом писали юнит-тесты. И это ни на что не повлияло.


Позволь задать вопрос: как бы ты писал юнит-тесты после продакшн-кода, но небольшими кусками.


Я бы написал немного продакшн-кода – достаточно, чтобы пройти пару тестов, а потом бы написал эти тесты.


Как бы ты понял, сколько кода нужно для удовлетворения пары тестов?


Я бы представил пару тестов, а потом написал бы удовлетворяющий их код. Потом бы написал тесты.


У тебя есть навыки в TDD; такой стиль мышления был бы очевидным для тебя, правда?


Мм… Хмм… Кажется, я понял, о чем ты. TLD'шники просто делали TDD в голове, а потом реализоывали все в обратном порядке.


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


Может быть это исследование не исследовало то, что было задумано.


Мне кажется они пытались исследовать порядок написания тестов, а не сам процесс TDD. В попытках уменьшить количество переменных, они избавились от них всех. Они заставили TLD-участников использовать процесс TDD короткими циклами. И это заставило участников работать над кодом и думать о тестах заранее.


ОК. Может быть. Но все же эти TLD'шники писали тесты в конце. Тесты показали как минимум то, что не нужно обязательно писать тесты вначале – как минимум если работа проходить короткими циклами.


Конечно. Эффективность TDD зависит от размера цикла, и в меньшей степени от первоочередности тестов. Мы пишем тесты заранее, потому что это подталкивает к сокращению длины циклов.


То есть это исследование показало, что люди, работающие короткими циклами, могут не волноваться о написании тестов заранее. Главное продолжать работать короткими циклами.


Похоже на справедливое заключение. Но, взгляни сюда. Участники работали над проблемой The Bowling Game. Это очень маленькая задача. Они сказали, что на все про все им понадобилось всего три часа.


Это важно?


Конечно. Польза в написании тестов заранее это дисциплина. Написание тестов заранее не дает циклу разрастаться. Сохраняет высокую степень покрытия на протяжении длительных периодов времени.


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


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


Ага. Ага. Исследование показывает именно это.


Так что в реальности исследование делало различие без отличия.


Ну… Хе-хе, они не обнаружили отличий, так что, пожалуй, да.


То есть исследование не показало, что TDD не работает, правда?


Нет, пожалуй, не показало.


Что же оно показало?


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

Поделиться с друзьями
-->

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


  1. xxvy
    11.11.2016 13:53
    +3

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


    1. bear11
      11.11.2016 15:23

      Почему это? Можно ведь «тесту после кода» просто не дать никакого кода и проверить его работу, не так ли?


    1. webkumo
      11.11.2016 17:47

      Coverage ничего не покажет?


      1. el777
        12.11.2016 00:15
        +3

        Ничего.
        Например, написал я недавно тест:

        self.assertEqual(str(obj), name)
        

        потом немного дописал и решил упростить:
        assert  str(obj), name
        

        Ошибку видите? А покрытие отличное — тесты прошлись по этой строке.


  1. AstarothAst
    11.11.2016 14:08
    +10

    Так TDD живо?! Ну, прям камень с души…


  1. ikovrigin
    11.11.2016 14:16
    +15

    Статья о том что в интернете можно найти кучу исследований «высосанных из пальца», а еще кучу неверных интерпретаций? Дочитал до конца и понял что потратил свое время в пустую, если вы еще не читали не делайте этого. :)


    1. MyDogTom
      11.11.2016 15:19
      +1

      Далеко ходить не надо. Вот это исследование, его обсуждение и выводы на хабре: TDD все еще сравнивают с TLD — мнения экспертов


      1. Scf
        11.11.2016 15:43

        Я вот после первого абзаца пролистал до комментариев :-)


    1. KasperGreen
      11.11.2016 17:29
      +4

      По статистике, 90% статистики высосано из пальца


  1. vintage
    11.11.2016 15:00
    +3

    Я бы представил пару тестов, а потом написал бы удовлетворяющий их код. Потом бы написал тесты.

    Глупость какая-то. Так поступили бы только фанаты TDD. Обычный программист бы просто реализовал некоторую часть функцинала и когда он бы с ней закончил и понял, что более менять его API не будет — только тогда уже стал думать какие тесты нужно написать и какие моменты нужно протестировать. А тестирование белого ящика эффективней, так как программист знает все внутренние критические точки. TDD же провоцирует тестировать чёрный ящик, когда реализации ещё нет и соответственно ты не знаешь при каких значениях у тебя может всё сломаться.


    1. lair
      11.11.2016 15:16

      А тестирование белого ящика эффективней, так как программист знает все внутренние критические точки.

      Как быть с ситуациями, когда это знание ошибочно?


      1. vintage
        11.11.2016 15:19

        Так же как и с ситуациями, когда этого знания нет.


        1. lair
          11.11.2016 15:22

          Когда знания нет, мы просто тестируем черный ящик. А вот когда знание есть, но ошибочно, мы тестируем белый ящик, но вот только тезис об эффективности вряд ли остается корректным.


          1. vintage
            11.11.2016 17:45

            Эффективность выше, но не 100% от недостижимого максимума.


            1. Fesor
              12.11.2016 03:37
              +1

              А стоимость поддержки таких тестов?


              1. vintage
                12.11.2016 09:31

                Такая же как и любых других тестов.


                1. Fesor
                  12.11.2016 16:33

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


                  1. vintage
                    12.11.2016 16:39

                    Термин "белый ящик" — это антитеза "чёрному ящику". А "чёрный ящик" — это модель системы, игнорирующая внутреннее устройство. Соответственно, тестирование белого ящика — это просто тестирование. Без делания вида, что можно полноценно покрыть систему тестами, не учитывая её внутренние особенности.


                    1. Fesor
                      12.11.2016 17:11
                      +1

                      не учитывая её внутренние особенности.

                      А какие внутренние особенности следует учитывать, кроме взаимодействия с внешним миром или другими объектами?


                      Я к тому что...


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

                      Я не вижу никакой разницы. Что те тесты что другие должны проверять пост/пред условия и инварианты, а что бы "не знать на чем будет ломаться" и не думать об этом можно просто использовать большие наборы рандомных данных (property based testing).


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


                      1. vintage
                        12.11.2016 18:37

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


                        1. Fesor
                          12.11.2016 21:42
                          +1

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


            1. lair
              12.11.2016 13:39
              +1

              (зависит от того, что вы понимаете под эффективностью)


              Когда вы делаете только белый ящик, ваше тестовое покрытие основано на реализации. И поэтому пропустить какой-то реальный ошибочный кейс вполне реально.


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


              (PS ваш кейс с четными/нечетными массивами в сортировке традиционно покрывается диапазоном автосгенеренных входных данных)


              1. vintage
                12.11.2016 14:16

                Когда вы делаете только белый ящик, ваше тестовое покрытие основано на реализации.

                Оно учитывает реализацию, добавляя дополнительные классы входных параметров. Все остальные классы оно не исключает.


                И поэтому пропустить какой-то реальный ошибочный кейс вполне реально.

                В любом случае есть шанс пропустить какой-то реальный ошибочный кейс.


                Так что эффективней — сначала тестирование как черного ящика, которое гарантирует соответствие требованиям

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


                ваш кейс с четными/нечетными массивами в сортировке традиционно покрывается диапазоном автосгенеренных входных данных

                Сгенерируйте "диапазон автосгенерированных данных" и я сгенерирую вам ошибочный кейс, который этим диапазоном детектирован не будет.


                1. lair
                  12.11.2016 14:19

                  Все остальные классы оно не исключает.

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


                  Сгенерируйте "диапазон автосгенерированных данных" и я сгенерирую вам ошибочный кейс, который этим диапазоном детектирован не будет.

                  Вот вам ваш же ответ:


                  В любом случае есть шанс пропустить какой-то реальный ошибочный кейс.


                  1. vintage
                    12.11.2016 15:52

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

                    Или наоборот: чёрный ящик — это белый ящик из которого вырезали часть логики.


                    1. Fesor
                      12.11.2016 16:34

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


                      1. vintage
                        12.11.2016 16:46

                        Вот у вас есть функция snakeToCamelCase( id : string ) : string. Что нужно замочить, чтобы протестировать эту функцию?


                        1. Fesor
                          13.11.2016 14:56

                          ничего, оно ж "чистое". Нет взаимодействия с внешним миром или зависимости от него — нет необходимости как-то что-то мокать.


                          Но и особенности реализации учитывать тут тоже не нужно. Функция snakeToCamelCase будет для нас черным ящиком. А если есть еще обратная операция, можно гонять на рандомных тестовых данных и тестить сразу две функции.


                          1. vintage
                            13.11.2016 16:10

                            А теперь смотрим на реализацию:


                            const toLocaleUpperCase = str => {
                                return str.replace( /i/g , "I" ).toLocaleUpperCase()
                            }
                            
                            const snakeTokenToUpperCaseCache = {}
                            
                            const snakeTokenToUpperCase = token => {
                                const cached = snakeTokenToUpperCaseCache[ token ]
                                if( cached ) return cached
                            
                                const letter = snakeTokenToUpperCaseCache[ token ] = toLocaleUpperCase( token[1] )
                                if( letter === token[1] ) throw new Error( `Wrong token: ${ JSON.stringify( token ) }`  )
                            
                                return letter
                            }
                            
                            const snakeToCamelCase = id => {
                                return id.replace( /(_.)/g , snakeTokenToUpperCase )
                            }

                            Всё ещё считаете её чистой?


                            1. Fesor
                              13.11.2016 16:32

                              В целом… если состояние изолировано в пределах этого модуля, то все ок. Сайд эффектов же снаружи это не будет вызывать. А вот если к "кэшу" имеет доступ не только эта функция — то тогда у нас проблемы, да. Точнее у автора кода проблемы.


                              1. vintage
                                13.11.2016 16:36

                                Чистота — это не только отсутствие сайд эффектов, но и отсутствие зависимости от изменяемого состояния. Данный код, например, не проходит следующий тест: https://habrahabr.ru/post/314994/#comment_9907672


                    1. lair
                      13.11.2016 13:23
                      +1

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


                      1. vintage
                        13.11.2016 13:35

                        Не вижу тут особой сложности. Я вижу лишь одну сложность, свойственную обоим ящикам — лень заморачиваться. Ну так в этом случае независимо от цвета ящика будет 1-2 теста, покрывающих положительные сценарии. Вы свои слова можете подтвердить хоть какими-то исследованиями или же судите по себе?


                        1. lair
                          13.11.2016 13:36

                          Вы свои слова можете подтвердить хоть какими-то исследованиями или же судите по себе?

                          Конечно, по себе сужу.


              1. tundrawolf_kiba
                12.11.2016 15:44

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


      1. areht
        13.11.2016 01:59

        Во-первых, если код и тесты пишет один человек, это знание есть.
        Во-вторых, оно ошибочно )

        Тестировать черный ящик может только посторонний.


        1. lair
          13.11.2016 13:24
          +4

          Тестировать черный ящик может только посторонний.

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


          1. areht
            13.11.2016 13:38

            Зато и тесты, и реализация подвержены общим когнитивным искажениям.

            > они менее подвержены влиянию этой самой реализации,

            Вы так говорите, как будто это что-то хорошее. Не подверженность влиянию реализации — не самоцель. А достигнутое «менее» может нас и не приблизить к хорошим тестам.


            1. lair
              13.11.2016 13:40

              Зато и тесты, и реализация подвержены общим когнитивным искажениям.

              Это не изменится в том случае, если я буду писать тесты после реализации.


              Вы так говорите, как будто это что-то хорошее.

              Да, я считаю, что это что-то хорошее, потому что в этом случае основным, что влияет на тесты, будут требования.


              (собственно, было более одного раза, когда я вылавливал ошибки в требованиях просто попытавшись написать на них тесткейсы)


              1. areht
                13.11.2016 13:59

                > Да, я считаю, что это что-то хорошее, потому что в этом случае основным, что влияет на тесты, будут требования.

                Боюсь основным, что влияет на тесты будут всё же когнитивные искажения )

                > (собственно, было более одного раза, когда я вылавливал ошибки в требованиях просто попытавшись написать на них тесткейсы)

                А разве TDD — это не по одному тесту за раз? (поприветствуем искажения)

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


                1. lair
                  13.11.2016 14:06

                  А разве TDD — это не по одному тесту за раз? (поприветствуем искажения)

                  А я и не говорил, что я придерживаюсь классического TDD. Я говорил "пишу тесты до реализации".


                  Было более одного раза, когда ошибку в требованиях я находил только в процессе реализации.

                  Так у меня тоже было, несомненно.


                1. Fesor
                  13.11.2016 15:02

                  А разве TDD — это не по одному тесту за раз? (поприветствуем искажения)

                  Ну Кент Бэк в своей книжке говорит что размер цикла определяется разработчиком. Если вы делаете что, и вы уверены в том что делаете — вы можете писать сразу больше тестов, больше кода и больше рефакторить, но как-только уверенность гаснет (слишком часто все идет немного не так как вы планировали) — лучше уменьшать размеры цикла.


                  Ну то есть жесткого "сначала один тест и потом один код" нет. Например вы вполне можете воспользоваться триангуляцией и написать сходу 2-3 тест кейса, что бы не отвлекаться потом.


                  1. areht
                    13.11.2016 16:12

                    Если есть чёткие требования, то тесткейсов можно уверенно сотню написать.


                    1. Fesor
                      13.11.2016 16:38

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


                      Но порой я так и делал. Я описывал по 20-30 тест кейсов того, что мне было важно проверить. Те кейсы, которые я считал важными. И писал я их вместе потому что мне не хотелось переключаться между продумыванием задачи, и реализацией оной. На этапе написания тестов я уже получал какой-то фидбэк, который позволял мне лучше понять что надо будет делать.


                      Но такое было всего пару раз.


            1. Fesor
              13.11.2016 15:00
              +1

              Не подверженность влиянию реализации — не самоцель.

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


              1. areht
                13.11.2016 15:59

                Я вижу в принципе затруднительным написать тест, который всерьёз знает не только о контракте метода, но и о внутренностях. И это не в контексте TDD/TLD и черно-белого ящика, а профпригодности.


                1. vintage
                  13.11.2016 16:17

                  Да-да, всякий, кто не согласен с вашими убеждениями — профнепригоден.


                  1. areht
                    13.11.2016 16:19

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


                    1. vintage
                      13.11.2016 16:32

                      test({
                          'caching should not affects syntax checking' () {
                              mustFail( ()=> snakeToCamelCase( 'привет__мир' ) )
                              mustFail( ()=> snakeToCamelCase( 'привет__мир' ) )
                          }
                      })

                      Код тут: https://habrahabr.ru/post/314994/#comment_9907640


                      1. Fesor
                        13.11.2016 16:46

                        Простите, а какова ценность данного тест кейса? Что вы пытаетесь проверить оным? В вашем примере вы получите исключение и результат никогда не будет записан в кэш. И второй вызов так же не дойдет до кэша. А в названии тест кейса что-то про кэширование.


                        Вывод я могу сделать такой — тесты ради тестов. По факту вы ничего не проверяете. И это как раз следствие того, что вы знаете что внутри используется кэш. И вы как бы знаете так же что при исключении до кэша не дойдет. Тогда зачем это проверять?


                        1. vintage
                          13.11.2016 16:55

                          Что вы пытаетесь проверить оным?

                          Вот это:


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



                          По факту вы ничего не проверяете.

                          А почему тест падает? ;-)


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

                          Я знаю, что оно не должно доходить. А чтобы убедиться, что это так, и нужны тесты.


                          1. Fesor
                            13.11.2016 17:22

                            А почему тест падает? ;-)

                            а он падает?)


                            Я знаю, что оно не должно доходить. А чтобы убедиться, что это так, и нужны тесты.

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


                            Да и с точки зрения покрытия кода тестами ваш тест кейс который всего-лишь два раза вызывает код, всеравно не дойдет до кэша и его не покроет. А позитивные тест кейсы — могут покрыть.


                            1. areht
                              13.11.2016 17:32

                              > Если вы удалите свой кэш из реализации, и прогоните тесты, и они не упадут

                              А при рефакторинге у вас регрессий не случается?


                              1. Fesor
                                13.11.2016 20:11

                                случается, но это значит что мне надо фиксить регрессию. Тесты трогать в этом случае нельзя.


                            1. vintage
                              13.11.2016 17:35

                              а он падает?)

                              Проверьте.


                              Если вы удалите свой кэш из реализации, и прогоните тесты, и они не упадут (а они не упадут из того что вы написали) — значит вы хреново покрыли код тестами.

                              Ну да, по хорошему надо ещё проверить, что значения действительно кешируются. И для этого (внезапно) нужен доступ к приватному состоянию модуля (и как следствие — зависимость от реализации). :-D


                              1. areht
                                13.11.2016 17:54
                                +1

                                > В требованиях лишь «экран должен открываться меньше, чем за секунду».

                                Не проверяйте кеш, проверяйте, что экран открывается за секунду.


                                1. vintage
                                  13.11.2016 18:08

                                  В данном случае проверяется не кеш, а отсутствие его влияния на результат.


                                  1. areht
                                    14.11.2016 00:50

                                    в «Ну да, по хорошему надо ещё проверить, что значения действительно кешируются»? Влияние на результат?


                                    1. vintage
                                      14.11.2016 02:31

                                      Вопрос в чём?


                              1. mird
                                13.11.2016 18:38

                                Нет. Нужна возможность замокать кеш. и проверить потом его состояние. Dependency Injection вам в помощь.


                                1. Fesor
                                  13.11.2016 20:13

                                  скорее вынести кэш в отдельную функцию и задекорировать. Это будет и с точки зрения читабельности лучше и с точки зрения тестируемости.


                                  p.s. dependency injection не нужен в языках с модулями. Просто инверсия управления.


                                1. vintage
                                  13.11.2016 20:19

                                  Вы предлагаете изменить интерфейс на такой исключительно ради тестов?


                                  snakeToCamelCase( id : string , cacher : ICacher ) : string


                                  1. areht
                                    14.11.2016 01:01

                                    Если говорить таким тоном — что угодно будет звучать глупостью.

                                    Если вы пишите не библиотеку, а код испоьлзуется в 2-х местах (один раз в боевом, и 42 раза в тестах), то… Изменять интерфейс «только ради тестов» — может быть и не такая плохая идея, по сравнению с другими способами залезть в приватное состояние.

                                    Впрочем, ваш пример переносит ответственность за _хранение_ состояния в вызывающий код.


                                    1. vintage
                                      14.11.2016 02:38
                                      +1

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


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


                                      1. areht
                                        14.11.2016 03:14

                                        1) Вы приватное состояние, простите, рефлекшеном проверять хотите?
                                        2) Инъекция, обычно, происходит в конструктор, а не как вы написали. Конструктор можно самому и не вызывать. Если у вас тесты зависят от внутренней реализации — вы их такими написали.


                                        1. Fesor
                                          14.11.2016 03:30

                                          Вы приватное состояние, простите, рефлекшеном проверять хотите?

                                          вы не путаете с java/c#/php? в node.js доступ к любому приватному состоянию модуля можно получить. Вопрос надо ли это.


                                          Инъекция, обычно, происходит в конструктор

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


                                          1. areht
                                            14.11.2016 03:49

                                            > в node.js доступ к любому приватному состоянию модуля можно получить.

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

                                            > Просто подгружаем модуль с зависимостью, и если что — мы всегда можем переопределить какой модуль загружать.

                                            Ну, то есть никакого «snakeToCamelCase( id: string, cacher: ICacher )» тоже нет, и интерфейс методов менять от наличия кеша тоже не надо?


                                            1. vintage
                                              14.11.2016 04:06
                                              +1

                                              Просто подгружаем модуль с зависимостью, и если что — мы всегда можем переопределить какой модуль загружать.

                                              Фактически это service locator.


                                              Ну, то есть никакого «snakeToCamelCase( id: string, cacher: ICacher )» тоже нет, и интерфейс методов менять от наличия кеша тоже не надо?

                                              Но нужно знать какой модуль мокать, а это детали внутренней реализации.


                                              1. areht
                                                14.11.2016 04:19

                                                > Но нужно знать какой модуль мокать, а это детали внутренней реализации.

                                                Нет, это зависимости.


                                                1. vintage
                                                  14.11.2016 05:08
                                                  +1

                                                  Зависимости не объявленные в интерфейсе — детали внутренней реализации.


                                                  1. areht
                                                    14.11.2016 05:47

                                                    Что вы здесь называете интерфейсом?

                                                    Объект подразумевает, что его кто-то где-то создаёт, и этот кто-то знает о зависимостях. Зависимости — это не детали внутренней реализации.

                                                    А уж где и как вы их объявляете…


                                                    1. vintage
                                                      14.11.2016 08:42

                                                      Fesor, говорил про модули вида:


                                                      const cacher = require( 'cacher' )
                                                      exports.snakeToCamelCase = id=> {
                                                          ...
                                                      }


                                                      1. areht
                                                        14.11.2016 09:00

                                                        Это ответ на что? Fesor был комментария 4 назад.


                                                    1. Fesor
                                                      14.11.2016 21:40

                                                      Объект подразумевает, что его кто-то где-то создаёт, и этот кто-то знает о зависимостях. Зависимости — это не детали внутренней реализации.

                                                      Вы оперируете "классическим ООП на классах". Если под "объектом" мы будем понимать любую хрень, которая инкапсулирует в себе данные и поведение, то модуль — это объект. А его интерфейс — то что он экспортирует. В этом случае "зависимостями модуля" будет орудовать сам модуль, у него есть require который по сути является сервис локатором.


                                                      И по сути зависимости модуля дело самого модуля. Inversion of control это конечно хорошо, но так или иначе вы должны объявить эти зависимости. В языках с динамической системой типов, где вы не можете явно задать типы зависимостей, вам все равно придется иметь дело с названиями модулей.


                                                      Ну то есть как ни крути, а настоящий dependency injection в nodejs не выйдет сделать, и вместо того, чтобы пытаться с этим бороться, лучше воспользоваться преимуществами того что есть.


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


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


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


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


                                                      1. areht
                                                        15.11.2016 04:48

                                                        > В этом случае «зависимостями модуля» будет орудовать сам модуль, у него есть require который по сути является сервис локатором.

                                                        Тут какая фигня… зависимость существует и до её «инверсии». Что бы из сервис локатора что-то достать — надо что бы кто-то это туда положил.


                                                        1. Fesor
                                                          15.11.2016 11:44

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


                                                          А реализации базирующиеся на dependency injection всеравно орудуют именами модулей, потому что подругому в javascript не выходит. Аналогичная история в python.


                                        1. vintage
                                          14.11.2016 04:03

                                          1. В зависимости от языка и тестового фреймворка. В D, например, тесты можно писать внутри модуля/класса. В JS/TS приватное состояние обычно не изолируется от внешнего мира.


                                          2. А, так вы предлагаете ещё и одну простую функцию завернуть в класс, завязать на какой-то DI и приправить пачкой интерфейсов только ради тестов? :-)

                                          interface IConverter {
                                              convert( str : string ) : string
                                          }
                                          
                                          interface ICacher< Value > {
                                              get( key : string ) : Value
                                              set( key : string , next : Value ) : void
                                              has( key : string ) : boolean
                                          }
                                          
                                          @DI.Provide( 'IConverter' )
                                          class SnakeToCamelCase {
                                          
                                              @DI.Inject( 'ICacher' )
                                              cacher : ICacher
                                          
                                              convert( str ) {
                                                  // ...
                                              }
                                          
                                          }


                                          1. areht
                                            14.11.2016 04:46

                                            1) В JS/TS приватное состояние обычно не изолируется от внешнего мира.

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

                                            Правда, тогда не вижу и нужды интерфейс переделывать ради инъекции.

                                            2) вы предлагаете ещё и одну простую функцию завернуть в класс

                                            Во-первых, у вас этой «простой» функции пока не выделено. Это стоит сделать хотя бы для читабельности.
                                            Во-вторых, новый класс — это 2 слова и 2 скобочки.

                                            > завязать на какой-то DI

                                            Лично у меня классы не знают про существование «какого-то» DI. Если вы берёте инструменты требующие завязки — не меня в этом винить надо )

                                            > и приправить пачкой интерфейсов только ради тестов?

                                            Ну, если вы хотите модно, молодёжно и с ICacher — приправляете, если нет — инжектируйте snakeTokenToUpperCaseCache «как есть».

                                            Я бы предложил вынести кеширование за пределы знания класса SnakeToCamelCase просто по SRP, но мы не ищем легких путей.


                                            1. vintage
                                              14.11.2016 05:09

                                              Давайте вы лучше код приведёте, а то телепат из меня не важный.


                                              1. areht
                                                14.11.2016 05:38

                                                код чего?


                                                1. vintage
                                                  14.11.2016 08:43

                                                  Код всего, что вы пока что описываете словами.


                              1. lair
                                13.11.2016 18:58
                                +2

                                Ну да, по хорошему надо ещё проверить, что значения действительно кешируются

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


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


                                1. vintage
                                  13.11.2016 20:27

                                  Нужно проверить, что кэш не имеет отрицательного влияния на работоспособность

                                  Для этого надо ещё и реализацию без кеша сделать.


                                  и что метод попадает в нужные границы производительности.

                                  Это вообще ни о чём. Это не показывает правильно ли работает кеширование и работает ли вообще.


                                  Я, впрочем, для второго пункта часто жульничаю, и проверяю, что ресурсоемкая операция выполняется единожды.

                                  Так и надо проверять логику работы. Жульничество это только с позиции религиозной веры в то, что любой тест должен тестировать исключительно публичный интерфейс. Эта догма ничем не обоснована.


                                  1. Fesor
                                    13.11.2016 20:38

                                    любой тест должен тестировать исключительно публичный интерфейс. Эта догма ничем не обоснована.

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


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


                                  1. lair
                                    13.11.2016 20:41
                                    +2

                                    Для этого надо ещё и реализацию без кеша сделать.

                                    Нет, не надо. Надо всего лишь проверить на соответствие требованиям (в том числе — неявным).


                                    Это вообще ни о чём. Это не показывает правильно ли работает кеширование и работает ли вообще.

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


                                    Жульничество это только с позиции религиозной веры в то, что любой тест должен тестировать исключительно публичный интерфейс.

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


                                    1. vintage
                                      14.11.2016 03:00

                                      Нет, не надо. Надо всего лишь проверить на соответствие требованиям (в том числе — неявным).

                                      Я зря цитирую что ли?


                                      Нужно проверить, что кэш не имеет отрицательного влияния на работоспособность

                                      Чтобы зафиксировать отрицательное влияние чего-то, нужно сравнить наличие чего-то с отсутствием чего-то. Это две реализации.




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

                                      Мне нужно, чтобы код, который я написал, работал так, как я запланировал. Если он работает не так, то это мина замедленного действия. Задача тестов — проверка корректности логики работы, а не только лишь проверка внешнего интерфейса. Второе бы сгодилось в случае 100% покрытия, но оно невозможно, кроме совсем тривиальных случаев (тут речь не о покрытии строк кода, а о полноценном покрытии). Поэтому приходится покрывать лишь по одному случаю из каждого класса эквивалентности и убеждаться, что модуль не остаётся после этого в некорректном состоянии.


                                      при этом не проверяя соответствие реальным требованиям.

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


                                      1. lair
                                        14.11.2016 12:18

                                        Я зря цитирую что ли?

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


                                        Мне нужно, чтобы код, который я написал, работал так, как я запланировал.

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


                                        Я вот себе ставлю требование "кеш должен кешировать, но не должен влиять на результат (в том числе и на интерфейс) — быть абсолютно прозрачным для потребителя".

                                        Поставили такое требование — ну что ж, ваше дело, опять-таки. Разницу между бизнес-требованиями и этими требованиями вам же держать в голове.


                                        Единственный способ протестировать кеш — так или иначе получить доступ к его состоянию, а это требует знания внутренней реализации.

                                        Ну, вот это точно неверно. Я уже упоминал как минимум еще один способ тестировать кэширование.


                                      1. VolCh
                                        15.11.2016 13:32
                                        +2

                                        Единственный способ протестировать кеш — так или иначе получить доступ к его состоянию, а это требует знания внутренней реализации.

                                        Разделяйте задачи тестирования кеша, модуля его использующего и их интеграции. Если кеш полностью инкапсулирован в модуль, то тестировать (речь про юнит-тесты) нужно только модуль, наличие у него сокрытого кеша никак на тесты влиять не должно, ведь «кеш должен… быть абсолютно прозрачным для потребителя», а юнит-тест такой же потребитель как основное приложение. Для юнит-теста наличие или отсутствие внутреннего кеша должно быть прозрачным. Если юнит-тест сможет получить доступ к кешу, значит и потребитель сможет.


                      1. areht
                        13.11.2016 16:48

                        1) Не вижу проблемы с «Даже если я вдруг решу „поменять“ способ, которым это реализовано от слова совсем.». Плохой пример, надо более лучше знать о внутренностях.
                        2) Идемпотентность — это контракт.

                        Поздравляю, вы профгодны.


                        1. vintage
                          13.11.2016 17:00

                          1. А должна быть проблема?


                          2. Вы проверяете идемпотентность каждой функции? Вот только честно.


                          3. Но как же так, я же использовал знание внутренностей, для обнаружения условий возможного нарушения контракта.


                          1. areht
                            13.11.2016 17:06

                            1) Должна быть, иначе нет знания о внутренностях. Ну, по определению из https://habrahabr.ru/post/314994/?reply_to=9907672#comment_9907552

                            2) Я вообще тесты пишу только если Care-o-meter шевелится. Идемпотентность там не единственное исключение.

                            3) Вы такой тест и в TDD могли написать. Ну и см. п.1.


                            1. vintage
                              13.11.2016 17:21

                              1. Это не мои слова. Я сагрился лишь на ваш комментарий :-)


                              2. Вот о том и речь. Наличие изменяемого состояния — сигнал к тому, что надо проверить идемпонетность.


                              3. Так я любой тест могу написать с любой методологией. Но для чёрного ящика я этот тест скорее всего не напишу, ибо если буду проверять идемпотентность каждой функции, то у меня будет куча "тестов ради тестов". Куда рациональней, проверять идемпотентность лишь тех функций, которые зависят от изменяемого состояния.


                              1. areht
                                13.11.2016 17:26

                                1) Ну, если вы не читаете тред даже на коммент выше… В общем, это ваша проблема.

                                2, 3) Наличие требования о кеше — тоже сигнал к тому, что надо проверить идемпонетность. А требование у вас есть, иначе кеша бы не было


                                1. vintage
                                  13.11.2016 17:37

                                  Кеш добавлен для оптимизации. В требованиях лишь "экран должен открываться меньше, чем за секунду".


                          1. Fesor
                            13.11.2016 17:27

                            Вы проверяете идемпотентность каждой функции? Вот только честно.

                            есть не идемпотентные функции. Идемпотентные функции проверять легко. И можно просто пользоваться всякими фэйкерами чтобы увеличить количество тест кейсов без необходимости увеличивать количество тестов.


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

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


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


                            1. vintage
                              13.11.2016 17:41

                              Идемпотентные функции проверять легко.

                              Ага, только увеличивает число тестов вдвое.


                              И можно просто пользоваться всякими фэйкерами чтобы увеличить количество тест кейсов без необходимости увеличивать количество тестов.

                              Что за самцы феек?


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

                              Давайте без телепатии. Запустите тест и убедитесь, что он падает, если не верите мне на слово.


                              И это вы тоже должны были знать из реализации а потому не должны были это проверять.

                              Ну или внимательнее посмотрите на реализацию.


                  1. Fesor
                    13.11.2016 16:44

                    давайте по другому. Когда у разработчика есть какие-то убеждения, которые он считает абсолютными — он профнепригоден. В общем, аля "TDD никогда не работает" или "TDD работает всегда". "Только черный ящик", "только белый ящик"… "Только ООП", "только функциональное программирование"....


                    1. vintage
                      13.11.2016 17:02

                      только белый ящик

                      А наука — это вера в отсутствие бога. ;-)


                1. Fesor
                  13.11.2016 16:41

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


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


                  У многих потом просто возникает желание удалить неработающий тест кейс… и многие так и делают. А толку тогда в таких тестах?


                  1. areht
                    13.11.2016 16:59
                    +2

                    Во мне есть уверенность, что надо делить тесты на тесты из требований, и «случайные» тесты. У них разных жизненный цикл. Первые удалять нельзя (без согласования с заказчиком). Но в требованиях написано 10%, остальное приходится додумывать, и писать на придуманное тесты.


                    1. Fesor
                      14.11.2016 01:57

                      Ну в целом так и есть, просто есть требования от клиента, и есть технические требования которые мы тоже хотим проверить. Вариант с кэшем — тоже требование, просто техническое.


                      1. areht
                        14.11.2016 02:33
                        +1

                        Что вы называете техническим требованием не от клиента?

                        От клиента есть требование, что экран должен открываться за секунду. Больное воображение разработчика родило кеш.

                        Тут любой тест на кеш — «излишне привязан к реализации». Но проблема то не в этом. Точнее, тут нет решения.

                        Требование заказчика — объективно, его мы проверять хотим. А вот кеш — решение субъективное, это не «тоже требование». Да что уж там, костыль. Если мы этот костыль и хотим защитить от регрессии, то как-то иначе, чем если нам заказчик написал, что это должен быть «кеш LRU размером 1000».


                        1. vintage
                          14.11.2016 03:11

                          Требования от клиента формализуются в виде "приёмочных тестов". Но приёмочным тестированием вопрос проверки корректности не ограничивается. Тесты должны покрывать всю логику, а не только ту, что "влияет на результат". И, кстати, о результатах..


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


                          1. areht
                            14.11.2016 03:20

                            > Тесты должны покрывать всю логику

                            Кому должны?

                            > Так вот, неявный результат тоже надо проверять.

                            Если надо — проверяйте.

                            У меня программа, например, логи пишет, на них тестов нет.

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


                            1. vintage
                              14.11.2016 04:13

                              Кому должны?

                              Тому, кто хочет быть уверен в корректности работы программы.


                              У меня программа, например, логи пишет, на них тестов нет.

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


                              императивное программирование не обязано приводить к побочным эффектам

                              А где я говорил, что оно должно?


                              а функциональное, на практике, может к ним приводить

                              функция в ФП не может. По определению. А вот монада — может. Но монада — не функция.


                              1. areht
                                14.11.2016 04:58
                                +1

                                > А если вам плевать пишутся логи корректно или нет, то скорее всего вам логи и не нужны.

                                Вам, конечно, виднее.


                              1. VolCh
                                15.11.2016 13:49
                                +1

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

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


                                1. vintage
                                  15.11.2016 17:17

                                  А потом окажется, что в лог вместо айпишника выводится пароль пользователя в открытом виде. Или приложение падает из-за того, что место на диске закончилось, так как в логи сваливался дамп всех файлов, что посылал пользователь.


                                  1. lair
                                    15.11.2016 17:47

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


                                    1. vintage
                                      15.11.2016 20:16

                                      Всегда есть неявные требования. Например: в приложении не должно быть бэкдоров, оно не должно втихоря майнить биткоины, оно не должно быть подвержено xss. И отсутствие явного указания на это в ТЗ вас нисколько не спасёт от укоризны.


                                      1. lair
                                        15.11.2016 21:49

                                        И вы, конечно, предлагаете написать на все эти неявные требования явные тесты?


                                        1. vintage
                                          15.11.2016 23:59

                                          Я предлагаю не притворяться будто этих требований нет.


                                          1. lair
                                            16.11.2016 00:35

                                            А не важно, в общем-то, есть они или нет — я спрашиваю, предлагаете ли вы написать на них явные тесты.


                                            1. vintage
                                              16.11.2016 08:59

                                              Разумеется. Если соответствующие проблемы не исключены архитектурно.


                                              1. VolCh
                                                16.11.2016 09:58
                                                +2

                                                Как вы видите тест на то, что в приложении нет бэкдоров или оно не майнит биткоины?


                                                1. vintage
                                                  16.11.2016 10:12

                                                  Если вы это не реализовывали, то и тесты на это не нужны ;-)


                                                  1. VolCh
                                                    16.11.2016 10:21

                                                    А если реализовывал по собственной инициативе и хотел бы скрыть от других? :)


                                                    1. vintage
                                                      16.11.2016 10:30

                                                      Значит тесты тоже стоит скрыть от других.


                                                  1. lair
                                                    16.11.2016 11:05
                                                    +2

                                                    Извините, но вы только что опровергли собственный же тезис, что надо писать на это явные тесты.


                                                    1. vintage
                                                      16.11.2016 14:39
                                                      -1

                                                      От себя-то их скрывать не надо.


                                                      1. lair
                                                        16.11.2016 14:40
                                                        -1

                                                        Теперь посмотрите, на какой именно комментарий я отвечаю, и задумайтесь, при чем тут вообще сокрытие.


                                                        1. vintage
                                                          16.11.2016 15:36
                                                          -2

                                                          Все претензии к Хабру.


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


                                                          1. lair
                                                            16.11.2016 16:08
                                                            -1

                                                            Ага, то есть если я запись файлов пользователя в логировании не реализовывал, или пароли не пишу, то тестов на это тоже не надо, правильно?


                                                            1. vintage
                                                              16.11.2016 18:15

                                                              И если написаны тесты на то что именно выводится. А то мало ли что в какой-то переменной вдруг окажется.


                                                              1. lair
                                                                16.11.2016 18:24
                                                                -1

                                                                Выводится то, что передали, с логгера взятки гладки.


                                                                1. vintage
                                                                  16.11.2016 19:40
                                                                  -1

                                                                  А при чём тут логгер?


                                                                  1. lair
                                                                    16.11.2016 21:10

                                                                    Logger: one that logs.


                                                                    1. vintage
                                                                      17.11.2016 01:01
                                                                      -2

                                                                      Spoon: one that eats.


                                      1. Fesor
                                        16.11.2016 08:55

                                        казалось бы, причем тут TDD раз уж речь уже не о юнит тестах и о тестировании нефункциональных требований?


                                        1. vintage
                                          16.11.2016 09:06

                                          Вполне себе функциональные. Да и в TDD можно не только юнит тесты применять.


                                          1. Fesor
                                            16.11.2016 09:58
                                            +1

                                            Вполне себе функциональные.

                                            Это с точки зрения разработчика, с точки зрения пользователя это нефункциональные требования. Защита от XSS и другого рода атак, производительность, все это является нефункциональными требованиями. Их отдельно описывают и отдельно тестируют.


                                            Вы можете разрабатывать библиотеки, которые помогут вам добиться этих нефункциональных требований, и с точки зрения библиотеки то, что они делают будут уже функциональными требованиями (фильтрация XSS например, или кэш, или балансировка какая-либо). Но в целом это далеко выходит за рамки юнит тестов, обычно используется в контексте e2e/приемочных тестов. А это уже совсем другая история.


                                            Да и в TDD можно не только юнит тесты применять.

                                            и называется это уже ATDD/BDD.


                                            p.s. я просто уже не вижу нити дискуссии. То что я вижу — тесты ради тестов.


                                            1. vintage
                                              16.11.2016 10:29

                                              с точки зрения пользователя это нефункциональные требования.

                                              https://ru.wikipedia.org/wiki/Требования_к_программному_обеспечению#.D0.92.D0.B8.D0.B4.D1.8B_.D1.82.D1.80.D0.B5.D0.B1.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D0.B9_.D0.BF.D0.BE_.D1.83.D1.80.D0.BE.D0.B2.D0.BD.D1.8F.D0.BC


                                              Защита от XSS

                                              Реализуется архитектурно. Или необходимо тестирование каждого вывода данных пользователя (экранирование спецсимволов и тп)


                                              производительность,

                                              Логика работы кеша — вполне себе функциональные требование. Даёт ли он положительный прирост производительности — другой вопрос.


                                              Вы можете разрабатывать библиотеки
                                              Но в целом это далеко выходит за рамки юнит тестов

                                              Библиотекам юнит тесты не нужны?


                                              и называется это уже ATDD/BDD.

                                              Это подвиды TDD, наряду с UTDD.


                                              я просто уже не вижу нити дискуссии. То что я вижу — тесты ради тестов.

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


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


                                  1. VolCh
                                    16.11.2016 09:57
                                    +1

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


                                    1. tundrawolf_kiba
                                      16.11.2016 13:48

                                      и называется это уже ATDD/BDD.

                                      Это подвиды TDD, наряду с UTDD.

                                      Все же BDD — это не подвид TDD. Это скорее дополнительный уровень абстракции для того, чтобы тесты были человеко-понятными. А вот если мы сочетаем BDD и TDD — тогда и получается ATDD.


    1. Cromathaar
      11.11.2016 15:32

      А можно пример критических точек, которые делают более выгодным тестирование белого ящика?


      1. vintage
        11.11.2016 17:52

        Видимо мой комментарий с примером не долетел до интернета..


        Классический пример: вам нужно определять углы треугольника по длинам сторон.
        На входе: три числа — длины сторон одной размерности
        На выходе должно быть: три числа — углы в радианах.


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


        1. Alex_ME
          11.11.2016 18:32

          Попробую предположить:
          — остроугольный
          — прямоугольный
          — вырожденный (C=A+B)
          — невозможный (C^2!=A^2+B^2)

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

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


          1. armature_current
            11.11.2016 19:42
            +1

            невозможный (C^2!=A^2+B^2)
            в каком смысле невозможный?


            1. Alex_ME
              11.11.2016 19:46

              Я сморозил глупость с формулой, это касается только прямоугольных треугольников.

              Но все равно можно взять такие длины отрезков, что получить треугольник будет нельзя (например, 10, 1, 1)

              Заголовок спойлера
              image


            1. Krypt
              12.11.2016 09:55

              > (C^2!=A^2+B^2)
              Может быть С > A + B?

              Бонус:
              Данные могут быть некорректными (A, B или C < 0)
              Данные могут быть «достаточно большими», что может вызвать переполнение (а могут и не вызвать, зависит от реализации)


          1. vintage
            11.11.2016 20:26

            Вы забыли тупоугольный, вырожденный с одним нулевым ребром, вырожденный в точку. А вместо "невозможного" вы описали "с углом ABC не равным полуПи" :-) Невозможный — это C>A+B


            Кроме того, надо проверить всё это с отрицательными значениями. И со значениями близкими к максимальным для заданного типа и минимально близким к 0. Ещё есть значения "бесконечность" и "не число", на которые тоже надо адекватно реагировать.


            1. Alex_ME
              11.11.2016 20:28

              Тупоугольный хотел написать, сам удивлся. Про «невозможный» выше написал, что ошибся.

              По остальному — согласен.


    1. Sing
      11.11.2016 15:32
      +3

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

      Первое, чёрный ящик позволяет отвлечься от того, как у вас там всё работает и представить себя «внешним наблюдателем», не замыленным взглядом взирающим на код.

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

      Третье, я не вижу вообще никакой причины использовать только чёрный или белый ящик, только TDD или TLD. Почему бы не написать тесты в начале, до того как вы знаете что внутри, а потом — после написания кода, обеспечивая покрытие?


      1. tundrawolf_kiba
        11.11.2016 15:39
        +1

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


        1. Cromathaar
          11.11.2016 15:48
          +1

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


      1. vintage
        11.11.2016 18:15
        +2

        1. Тестировать чёрный ящик может только телепат. Вот вам ещё пример — реализуете вы алгоритм сортировки. Тестируя чёрный ящик, вы возьмёте: пустой массив, отсортированный массив из 2 элементов, обратносортированный массив из 2 элементов, может ещё добавите сортировку массива из 10 элементов, поиграетесь с отрицательными числами, с дубликатами. Тесты написаны, реализация готова, запускаем в продакшен, продакшен падает, когда число элементов становится нечётным больше 2. А всё потому, что вы реализовали quicksort, который активно использует половинное деление, но округляете не в ту сторону. Чтобы узнать, что входные параметры делятся на два класса (с чётным и нечётным числом элементов больше 2), вам нужно быть в курсе внутренней реализации, а именно наличия деления числа элементов на 2.


        2. Я вам привёл пример, как знание внутренней кухни позволяет учесть больше вариантов. А вот когда тесты уже написаны, а реализация их проходит, то и появляется соблазн больше тестов не писать, чтобы никто не заподозрил тебя в белом ящецизме. :-)


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


        1. tundrawolf_kiba
          11.11.2016 19:45
          -1

          1) А спецификации на что? Если я знаю спецификацию, и если она создана качественно( а я непременно обращусь с вопросами к аналитику, если увижу в ней неоднозначности ), то я напишу тесты согласно этой спецификации, и их внутренняя реализация меня не будет волновать, какая разница для заказчика, как реализовано, если удовлетворяет требованиям?
          2) Чтобы не впасть в такой соблазн — нужно просто помнить о «парадоксе пестицида»
          3) Опять же — это если нет спецификации


          1. vintage
            11.11.2016 20:39
            +1

            1) Каким образом вам тут поможет спецификация? Или вы ожидаете детализированное описание алгоритма сортировки? А программист в этой цепочке тогда зачем? Скармливаем "качественно созданную спецификацию" парсеру спецификаций и на выходе получаем готовую программу. В былые времена такие парсеры спецификаций называли компиляторами. Спецификации — исходными кодами. А аналитиков, которые их писали — программистами. Как сейчас — не знаю, видимо я отстал от жизни. :-)


            3) Вы точно не робот? Я не видел ещё ни одной спецификации с таким уровнём детализации.


            1. tundrawolf_kiba
              11.11.2016 22:28

              >Каким образом вам тут поможет спецификация?
              Ну как бы спецификация описывает то, что пользователь ожидает то программы. При тестировании — проверяется соответствие требованиям. Если программа соответствует требованиям — значит можно релизиться. Нюансы реализации могут интересовать или разработчиков или архитектора, но зачем он заказчику?
              Я не понимаю, зачем вы утрируете? Или вы пишите ПО без ТЗ? Мне если честно сложновато в это поверить.


              1. vintage
                11.11.2016 23:04

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


                А так, ТЗ у меня выглядит так: заказчик хочет приложение с примерно таким функционалом, проверь реализуемость, напиши "проектное решение", согласуй с заказчиком, реализуй, а потом пройди опытную эксплуатацию. При это если в результате всё работает не так как ожидает пользователь, то это надо исправить. Даже если в "проектном решении" написано по другому — значит неправильно его составил и нужно исправлять его тоже и пересогласовывать.


                1. tundrawolf_kiba
                  11.11.2016 23:29
                  -1

                  Что-то не могу представить спецификацию ПО с квантором всеобщности.

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


                  1. vintage
                    12.11.2016 00:39

                    "Щёлчок по любому элементу списка должен приводить к открытию инфо панели с подробной информацией по этому элементу" и тд.


                    Всё сложно. Бывает важные вещи согласуются месяцами, а сроки двигать никто не хочет. И приходится реализовывать несколько вариантов или же самый вероятный с прицелом, что придётся всё переиграть. Этакий Корпоративный Эджайл :-D


                    1. tundrawolf_kiba
                      12.11.2016 01:06

                      «Щёлчок по любому элементу списка должен приводить к открытию инфо панели с подробной информацией по этому элементу» и тд.


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

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

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


                      1. vintage
                        12.11.2016 01:18

                        У вас в спецификациях описываются классы эквивалентности для каждой функции?


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


                        1. tundrawolf_kiba
                          12.11.2016 01:53

                          У вас в спецификациях описываются классы эквивалентности для каждой функции?

                          В спецификации ПО — нет, в спецификации тестирования — уже может иметь смысл. По проще всего выделить классы эквивалентности в момент написания тесткейсов.

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


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


        1. Sing
          11.11.2016 21:07
          +1

          Тестировать чёрный ящик может только телепат.

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

          В итоге, вы ограничили себя только одним методом поиска ошибок, за что и поплатились. Я даже не хочу рассыпаться рассуждениями о том, что после изменения алгоритма, вам, судя по всему, придётся переписывать тесты.
          Я вам привёл пример, как знание внутренней кухни позволяет учесть больше вариантов.
          А может и меньше. В вашем примере вы могли учесть только два своих «класса» данных — чётный и нечётный, и забить на, например, совпадения.
          Вот и получается, что в случае, когда ты ещё не знаешь точно какой у тебя будет интерфейс (а это большинство случаев)
          В смысле — интерфейс? У вас функция сортировки какой-то необычный интерфейс имеет, который изменился во время реализации?


          1. vintage
            11.11.2016 21:31

            Без интеграционных тестов? Без тестирования вручную? Да, похоже на работу телепата.

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


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

            То, что ошибка пролезла через все стадии тестирования не говорит о том, что этих других стадий не было. Реальная ошибка может проявляться лишь на классе "число задач на сегодня кратное 256" и вы будете очень долго ловить этот баг в "чёрном ящике" по багрепортам вида "иногда не показывается последняя задача, не понятно при каких обстоятельствах".


            Я даже не хочу рассыпаться рассуждениями о том, что после изменения алгоритма, вам, судя по всему, придётся переписывать тесты.

            Именно так. У нового алгоритма будут другие слабые места и неплохо было бы шмальнуть по ним для профилактики.


            В вашем примере вы могли учесть ТОЛЬКО два своих «класса» данных — чётный и нечётный, и забить на, например, совпадения.

            Забить на совпадения я могу независимо от цвета ящика.


            В смысле — интерфейс? У вас функция сортировки какой-то необычный интерфейс имеет, который изменился во время реализации?

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


            1. Deerenaros
              12.11.2016 13:48

              Ну отлично. Давайте из одной в крайности — другую. Но начнём с другого, товарищ удобный интерфейс. Потому что то, что вы описали не совсем-то интерфейс. И хотя возможность задавать компаратор и является интерфейсом, товарищ, любая сортировка используя механизм сравнения пар по умолчанию должна иметь некий компаратор. Пускай он будет стандартный, пускай он будет задаваться — это не то, чтобы интерфейс, это здравый смысл.
              Продолжим тем, что когда любому методу задаётся на вход контейнер, где-то сразу в тесте после подачи нулей и нулов проверяется работа с чётным-нечётным. Это такая галочка есть у опытных писателей тестов.
              Было бы куда более убедительно, если бы вы взяли, например, некий специфичный процессор, у которого, например, 7 битовые слова с каким-нибудь особенным управляющим сигналом. Чёрт его знает, что это за зверь, но не имея представления о реализации сигнала или о доступных машинных словах — довольно тяжело делать тесты. Но признаемся сами себе — это, как говорят американцы, булшит. Не представляю, кто доверил бы написание тестов такому специфичному процессору не удостоверившись в вашей компетенции хотя бы по чтению даташитов.
              И, наконец, покуда вы знаете узкие места вашей реализации, в чём проблема включить грёбанный котелок и громко вслух проговорить узкие места алгоритма? Совесть за слух окружающих скорбит? Гордость за чувства окружающих теребит? Или банальное неумение читать собственный код? Ну, тут уж кто во что горазд, товарищ универсальная реализация.


              1. vintage
                12.11.2016 15:43

                то, что вы описали не совсем-то интерфейс

                Да нет, я описывал исключительно интерфейсы:


                int[] sort( int[] ints );
                
                Item[] sort( Item )( Item[] numbers ) if( isNumeric!Item );
                
                Item[] sort( Item )( Item[] items ) if( is( typeof( Item.init > Item.init ) ) );
                
                Item[] sort( Item )( Item[] items , int delegate( Item a , Item b ) comparator ) if( is( typeof( Item.init > Item.init ) ) );
                
                Item[] sort( Item , alias comparator = q{ a > b } )( Item[] items ) if( is( typeof( Item.init > Item.init ) ) );
                
                ElementType!Range[] sort( Range , alias comparator = q{ a > b } )( Range range ) pure if( isInputRange!Range && is( typeof( ElementType!Range.init > ElementType!Range.init ) ) )

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

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


                Дальнейший бессвязный поток желчи оставлю без комментариев.


    1. JediPhilosopher
      11.11.2016 15:41

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


      1. vintage
        11.11.2016 18:31

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


    1. deniskreshikhin
      11.11.2016 15:45
      +1

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


      1. vintage
        11.11.2016 18:43

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


        Так много всего возможно. И всё так неоднозначно. И столько всего нужно учитывать, принимая решение о рефакторинге. Более важного, чем догмат "тестирование приватного метода — зло".


        1. deniskreshikhin
          11.11.2016 19:55

          Так много всего возможно.

          Есть такой принцип в логике ex falso quodlibet, дословно «из ложного все что угодно». Если из чего-то проистекает слишком много вариантов, то это что-то скорее всего ошибочно.

          Странно, что вас не настораживает приведенный вами список.


    1. Delphinum
      11.11.2016 17:48

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


      1. VolCh
        11.11.2016 17:58

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


        1. Delphinum
          11.11.2016 18:04

          Поведение в критических точках и не должно быть описано, они просто должны быть объявлены таковыми, а тесты проверят поведение, и не важно, написаны они до или после реализации.


  1. greendimka
    11.11.2016 15:28
    +3

    Ничто не работает, если используется игнорируя сигналы мозга.
    TDD, BDD, DDD, и всё всё всё остальное — есть инструменты. Однако в последние годы у программистов появилась чёткая тенденция всё воспринимать как серебряную пулю. Доходит до абсурдных ситуаций. Но не буду вдаваться в программирование.
    Попробуйте построить дом только молотком. Или только отвёрткой. Или только пилой. Выкиньте все остальные инструменты — оставьте только болгарку! Провозгласите всех, пользующихся наждачной бумагой — пережитком прошлого. Вы видите каменщика? Объясните ему, что его подход устарел, а уже через полгода он не найдёт работу. Будьте тру-айтишником в строительстве.
    "Ваши методы фуфло!" (ваши методы по версии тех, кто пользуется другими методами.


    Перестаньте искать серебряные пули. Просто беритесь и делайте. Уместные инструменты вам в помощь.


    1. boldyrev_gene
      12.11.2016 13:49

      Простите, но ваше сравнение невероятно гипербализированно. В отличи от IT в строительстве все же новые технологии появляются куда реже, так что куда более уместно говорить «хватит жить в пещере», если предерживатся темы, чем предлагать отказатся от какой-то технологии, конкурентов которой за столетия толком и не появилось то.


      1. greendimka
        12.11.2016 14:22

        Вы правы — я гиперболизирую. Но именно для того, чтобы донести мысль. Представьте себе, что сегодня выпустили новый, великолепный инструмент, полностью заменяющий молоток — забивает идеально, не бьет по пальцам, работает быстрее быстрого. И представьте, что строители поступят типично по айтишному — мало того, что начнут повсеместно выкидывать молотки ([irony]ведь у всех есть время и деньги на внедрение нового инструмента[/irony]), но и начнут сносить все строения, построенные молотком — с целью сейчас же перестроить их с помощью нового инструмента. Глупо будет, не так ли?


        А между прочим такую картину в IT приходится наблюдать постоянно. Появляется новый инструмент, новая методология, новый подход — и старый мгновенно называют злом и открещиваются. Хотя старый отлично работает. Он плохой — просто потому, что не новый.


        Моя мысль проста: не выкидывайте старое. Изучайте новое — и объединяйте. Слепая замена — стоит очень дорого. И каждому инструменту — своё место.


        Не идите на поводу у статей. Прочитав статью подумайте: прав автор или ошибается.


        1. boldyrev_gene
          12.11.2016 15:18

          Ну если бы IT-компаниями управляли программисты, то может так оно и было, но вот не такое уж и частое явление, что руководство компании готово выделить ресурсы на «актуализацию» технологий. Например, на моей текущей работе уже достаточно долго никак не могу перейти с SVN на Git, не говоря уже о многих других технологиях, которые все еще используются, хотя потеряли свою актуальность уже не один год назад, из-за чего поддержка и развитие проекта обходится куда дороже, чем могла бы… Ну а вообще не спорю с Вами, что нужно выбирать подходящий инструмент под задачу, а не решать задачу каким либо инструментом только потому, что он новый и да, нужно находить возможность вводить более актуальные технологии параллельно с уже имеющиеся кодовой базой, не задаваясь целью просто взять и переписать все наработки.


  1. Fector
    11.11.2016 15:34

    Такие исследования генерируют холивары.


  1. VolCh
    11.11.2016 17:54
    +1

    Думаю, практически нет разницы когда физически писать тесты, если продумываешь их перед написанием кода. Ну то есть в одних ситуациях лучше бы было до, в других после, а в целом то на то и выходит. Но TDD обязывает продумывать тесты перед написанием кода, а TLD — нет.


  1. M-A-XG
    11.11.2016 18:03

    >Тесты показали как минимум то, что не нужно обязательно писать тесты вначале – как минимум если работа проходить короткими циклами.

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

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


    1. Delphinum
      11.11.2016 18:07

      А разве сама структура тест кейса не определяет размерность итерации?


      1. M-A-XG
        11.11.2016 20:50

        Каким же образом?
        Где это явно описано? :)


        1. Delphinum
          12.11.2016 02:03

          Ну конечно, есть паттерны тестирования, которые описывают размерность конкретных тест-кейсов, а так же ограничивают функциональность, которую они охватывают.


          1. M-A-XG
            12.11.2016 18:30

            О, и тут есть паттерны. :)
            Конкретно на хабре можете привести ссылки, где говорится о размере итерации.


            1. Delphinum
              12.11.2016 20:20

              На хабре не встречал статей на эту тему, могу порекомендовать книгу: «Шаблоны тестирования xUnit. Рефакторинг кода тестов» Джерард Месарош.


  1. MetaDone
    11.11.2016 21:39

    мне кажется что без разницы, что писать сначала — код или тесты — главное чтоб выбранная методология повышала эффективность и стабильность конечного продукта


    1. tundrawolf_kiba
      11.11.2016 22:33

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


      1. gandjustas
        12.11.2016 16:15

        Requirements Review и Design Review в этом плане гораздо выгоднее, но что-то не видно вокруг них такого хайпа.


  1. alex_blank
    11.11.2016 22:16

    1. Fesor
      12.11.2016 03:39

      Выводы неверны… юнит тесты надо дополнять интеграционными, это да, но интеграционные тесты не замена юнит тестам.


      Be humble about what tests can achieve. Tests don’t improve quality: developers do.

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


      В целом по теме юнит тестов я обычно рекомендую посмотреть сие видео: The Magic Tricks of Testing by Sandi Metz


    1. cashby
      12.11.2016 14:21

      Вам ссылку в копилку, использовать в зависимости от настроения :)
      Integrated tests are scam


  1. sentyaev
    12.11.2016 04:41

    TDD или TLD — неважно, главное что-бы не TBD


    1. tundrawolf_kiba
      12.11.2016 15:47

      Можете пояснить, что такое TBD — я несколько вариантов нашел, но думаю, что они неверные…


      1. sentyaev
        13.11.2016 01:54

        to be defined, to be done, etc


  1. CTAPOBEP
    12.11.2016 13:49

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


  1. stepanp
    14.11.2016 01:27
    -2

    TDD убивает творческую составляющую разработки, это все что нужно о нем знать.