Я думаю, это не получится показать быстро и в виде текста, скорее всего это нужно делать на видео.
Тут нужно показывать не самый простой проект, написанный по TDD с тестами, которые ничего не знают про имплементацию проверяемой функциональности, то, как написаны тесты и то, как они при этом помогают рефакторингу. Из того, что на эту тему есть в сети, мне JBrains с лету приходит в голову вышеупомянутый (у него есть курс The World's Best Intro to TDD, действительно очень качественное введение в TDD), хотя наверняка есть и другие примеры. Но они вряд ли будут маленькими.
Я чтобы этому научиться уже не первый месяц в свободное время пишу код в паре с людьми, которые давно (некоторые по 20 лет и больше) в этой тусовке и участвую вместе с ними в mob programming сессиях.
Поэтому я тут наверно только могу порекомендовать, куда копать, если дальше интересно. Копать в сторону decoupling тестов от имплеметации. Про это есть целая школа TDD (Chicago, classicist). Можно про них почитать, можно посмотреть курс JBrains, у которого хорошо показаны тесты на фукнциональность которые не завязаны на конкретную имплементацию, есть хорошая статья про это у Martin Fowler "Mocks Aren't Stubs" (https://martinfowler.com/articles/mocksArentStubs.html), и еще вот тут есть отличный ответ на ту же тему от perfectionisthttps://softwareengineering.stackexchange.com/questions/5898/how-do-you-keep-your-unit-tests-working-when-refactoring (начинается "Contrary to the other answers...").
Еще из людей, у которых скорее всего это можно увидеть на видео, вроде бы у Uncle Bob были примеры, он тоже классицист TDD-шный, хотя я не особый его фанат. У Geepaw Hill есть хорошие видео. Еще приходит на ум James Shore, у которого тоже много хороших видео в свободном доступе и можно найти примеры хорошего кода на JS с TDD.
Упс, только я тоже ежа с апельсином спутал выше. Про mockist vs classicist TDD школу, мокисты - это Лондонская школа. : ) Чикаго - classicist и как раз они тестируют все через state и избегают знания имлементации. Сорри!
Я люблю тесты, пишу их сам и заставляю других по возможности, у меня проблема только с TDD
Я тоже в общем-то большую часть жизни так работал. Пишешь код, потом несколько дней на тесты. И это обычно очень унылые несколько дней; я очень ответственный человек, поэтому пишу несмотря на то, что мне при этом скучно и уныло.
Я тоже пробовал TDD когда-то раньше, но наткнулся на несколько практических проблем, которые было некому подсказать как решать, и сдулся, решив, что это не для меня и я лучше по старинке.
Так вот самым сильным аргументом для того, чтобы попробовать TDD еще раз, на этот раз с наставником, стало то, что TDD is more fun. Писать тесты вместе с кодом - занятие гораздо более веселое и интересное. И для меня это реально оказалось так.
Плюс в работе сильно помогает быстрый фидбек от тестов; я раньше постоянно запускал программу, чтобы проверить, что все работает. Фидбек от тестов в разы быстрее и более того, автоматический -- не нужно переживать, что ты что-то сломал во время рефакторинга и перепроверять снова и снова то, что ты уже раньше протестировал ручками.
У меня на практике обычно вот эта часть, что вы назвали "через добавление 10 ифчиков, а потом заменяете это на нормальный алгоритм" вызывала трудности, т.к. "нормальная" реализация всегда приводила к переименованию\удалению интерфейсов\классов\методов\аргументов, перестройке всей структуктуры кода. После чего все ранее написанные тесты грубо говоря проще выкинуть и написать заново. Как вы с этим боролись?
Декаплингом тестов от имлементации (тест не должен знать, как именно тестируемая фукциональность имлементирована, т.е. не должно быть verify (проверок вызова метода зависимого класса внутри тестируемой функции) и очень помогает сильно ограничить использование автомокеров). При этом подход к тестам довольно сильно меняется. Вместо того, чтобы писать тестовый класс на конкретный класс реализации (например, Multiplier -> MultiplierTest, 1:1), тест пишется на конкретную функциональность ("умножение чисел").
Функциональность - это как раз то, что скорее всего будет оставаться гораздо более стабильным, чем реализация; ведь вы эту фичу не просто так добавили, а потому что бизнес/пользователи попросили. Вы можете и скорее всего дальше будете её усложнять, но нечасто просто выкините саму фичу из того, что делает ваша программа.
Я в общем только очень недавно увидел, что такое бывает, и это офигительно. Рефакторишь код так, что уже давно прошел все пределы того, когда тесты, написанные по-старому, уже бы тысячу раз сломались "не по делу", а тесты тебе вместо того, чтобы мешать, помогают и подскзаывают, если ты действительно чего-то сломал. Но если не сломал, а просто поменял структуру кода - они не ломаются. Для меня это был полный снос башки.
Хороший пример того, как это делается, можно посмотреть в TDD курсе у J.B. Rainsberger (JBrains).
Конечно тут же встает вопрос, что такое юнит и что такое юнит тест, и в этот холивар я точно лезть не хочу : )
+1 к "Вот только все эти абсолюты к самому TDD имеют мало отношения, разве нет".
Владимир, вас, похоже, сильно травмировали, возможно не очень адекватные коллеги. Бывает.
Но у вас реально очень превратные представления о TDD, которые к реально практике отношения имеют очень мало от слова совсем.
100% покрытие вообще не является целью TDD. Это глупо и никому не нужно, потому что вот именно в этом случае придется писать совершенно нелепые тесты на сеттеры и геттеры и прочую ерунду. См. ссылку на то, как пишет тесты Кент Бек, которую давали выше. Никто из тех, кто работает по TDD из тех, с кем я лично общаюсь, не считает 100% покытие полезным, более того, в целом тестовое покрытие - метрика неоднозначная и проверяет только то, что бранч был вызван. Ничего про качество теста, вплоть тупо до наличия ассерта, оно не говорит.
Тесты как документация не является целью TDD! Совсем. Вы не с BDD перепутали?
Насчет того, что все примеры, которые приводят сторонники TDD, простые. Есть стримеры, которые показывают, как они работают над серьезными проектами и следуют TDD, есть видео, которые эти люди записывают и показывают. Из известных людей Uncle Bob, менее известных можно легко нагуглить при желании.
"Да, и что бы 2 раза не вставать замечу, что когда тесты покрывают код целиком и полностью, не оставляя живого места, любая попытка изменения функции или ее интерфейса приводит к дикому баттхёрту." - а вот это очень интересная тема. Дело вообще не в тестах, дело в моках. Есть две школы TDD, Чикагская и Лондонская. Чикаго - "мокисты", которые изолируют каждый класс и мокируют все зависимости. Лондонская школа избегает того, чтобы тесты знали, КАК метод делает то, что он делает - они проверяют только результат! И при таком подходе тесты - первейший инструмент рефакторинга, который помогает менять код, не ломая его. В этом собственно и основной смысл тестов.
"TDD позволяет сформировать хорошую архитектуру на ранних этапах, которую потом почти не придется менять (как и код), ибо TDD заставляет подумать до написания кода". TDD помогает писать код, который легко тестировать (юнит тестами, да). Задачи не менять её не стоит вообще! Совсем наоборот, при добавлении любой фичи - смотришь на то, а не поменялось ли чего более глобально, как эту фичу встроить; рефакторишь всегда! И тесты, если ты их сохранил гибкими и не знающими имлеметации того, что они тестируют, очень в этом помогают.
"Юнит тесты хорошо, а другие – плохо". Ну блин. Откуда вы это взяли? То, что есть тестовая пирамида, не означает, что не нужно ничего, кроме юнитов. И опять же, TDD тут мало при чем.
"Вот только легко тестируемый код не дает никаких гарантий качества получившегося приложения." - какие-то дает, но конечно не панацея. Если сначала не поговорили с клиентом или поговорили криво и сделали не то, то все в помойку.
В общем и целом, понятно, что вас сильно травмировали возможно не очень адекватные коллеги. Но вы реально написали статью, очень смутно представляя, что такое TDD на практике и зачем оно, и спорили всю статью сами с собой.
TDD очень обманчиво кажется простым и поэтому многие считают, что уже все про TDD поняли. На практике начиная пробовать TDD натыкаешься на кучу вопросов и проблем. Поэтому самый лучший вариант реально научиться и попрбовать - это поработать с кем-то, кто уже в этом реальный профи. Тогда и вопросы можно задать.
Одна из интересных неупомянутых проблем, например, это что делать, чтобы не писать в помойку. Обычная тема новичка - написать отличный класс, который потом окажется вообще не нужным, и его придется целиком выкинуть. Существуют возможности делать наброски даже по TDD, нащупывать первоначальную архетектуру - которую потом тридцать раз может быть поменяешь - которые помогают этого избежать.
Еще на тему стабильных тестов, которые не ломаются от каждого чиха:
https://www.youtube.com/watch?v=URSWYvyc42M&ab_channel=Confreaks
Да, и если вам не особенно интересна тема TDD, то скорее всего действительно не стоит тратить время и деньги.
James Shore очень толковый чел, +1.
Я думаю, это не получится показать быстро и в виде текста, скорее всего это нужно делать на видео.
Тут нужно показывать не самый простой проект, написанный по TDD с тестами, которые ничего не знают про имплементацию проверяемой функциональности, то, как написаны тесты и то, как они при этом помогают рефакторингу. Из того, что на эту тему есть в сети, мне JBrains с лету приходит в голову вышеупомянутый (у него есть курс The World's Best Intro to TDD, действительно очень качественное введение в TDD), хотя наверняка есть и другие примеры. Но они вряд ли будут маленькими.
Я чтобы этому научиться уже не первый месяц в свободное время пишу код в паре с людьми, которые давно (некоторые по 20 лет и больше) в этой тусовке и участвую вместе с ними в mob programming сессиях.
Поэтому я тут наверно только могу порекомендовать, куда копать, если дальше интересно. Копать в сторону decoupling тестов от имплеметации. Про это есть целая школа TDD (Chicago, classicist). Можно про них почитать, можно посмотреть курс JBrains, у которого хорошо показаны тесты на фукнциональность которые не завязаны на конкретную имплементацию, есть хорошая статья про это у Martin Fowler "Mocks Aren't Stubs" (https://martinfowler.com/articles/mocksArentStubs.html), и еще вот тут есть отличный ответ на ту же тему от
perfectionisthttps://softwareengineering.stackexchange.com/questions/5898/how-do-you-keep-your-unit-tests-working-when-refactoring (начинается "Contrary to the other answers...").Еще из людей, у которых скорее всего это можно увидеть на видео, вроде бы у Uncle Bob были примеры, он тоже классицист TDD-шный, хотя я не особый его фанат. У Geepaw Hill есть хорошие видео. Еще приходит на ум James Shore, у которого тоже много хороших видео в свободном доступе и можно найти примеры хорошего кода на JS с TDD.
Упс, только я тоже ежа с апельсином спутал выше. Про mockist vs classicist TDD школу, мокисты - это Лондонская школа. : ) Чикаго - classicist и как раз они тестируют все через state и избегают знания имлементации. Сорри!
Я люблю тесты, пишу их сам и заставляю других по возможности, у меня проблема только с TDDЯ тоже в общем-то большую часть жизни так работал. Пишешь код, потом несколько дней на тесты. И это обычно очень унылые несколько дней; я очень ответственный человек, поэтому пишу несмотря на то, что мне при этом скучно и уныло.
Я тоже пробовал TDD когда-то раньше, но наткнулся на несколько практических проблем, которые было некому подсказать как решать, и сдулся, решив, что это не для меня и я лучше по старинке.
Так вот самым сильным аргументом для того, чтобы попробовать TDD еще раз, на этот раз с наставником, стало то, что TDD is more fun. Писать тесты вместе с кодом - занятие гораздо более веселое и интересное. И для меня это реально оказалось так.
Плюс в работе сильно помогает быстрый фидбек от тестов; я раньше постоянно запускал программу, чтобы проверить, что все работает. Фидбек от тестов в разы быстрее и более того, автоматический -- не нужно переживать, что ты что-то сломал во время рефакторинга и перепроверять снова и снова то, что ты уже раньше протестировал ручками.
У меня на практике обычно вот эта часть, что вы назвали "через добавление 10 ифчиков, а потом заменяете это на нормальный алгоритм" вызывала трудности, т.к. "нормальная" реализация всегда приводила к переименованию\удалению интерфейсов\классов\методов\аргументов, перестройке всей структуктуры кода. После чего все ранее написанные тесты грубо говоря проще выкинуть и написать заново. Как вы с этим боролись?Декаплингом тестов от имлементации (тест не должен знать, как именно тестируемая фукциональность имлементирована, т.е. не должно быть verify (проверок вызова метода зависимого класса внутри тестируемой функции) и очень помогает сильно ограничить использование автомокеров). При этом подход к тестам довольно сильно меняется. Вместо того, чтобы писать тестовый класс на конкретный класс реализации (например, Multiplier -> MultiplierTest, 1:1), тест пишется на конкретную функциональность ("умножение чисел").
Функциональность - это как раз то, что скорее всего будет оставаться гораздо более стабильным, чем реализация; ведь вы эту фичу не просто так добавили, а потому что бизнес/пользователи попросили. Вы можете и скорее всего дальше будете её усложнять, но нечасто просто выкините саму фичу из того, что делает ваша программа.
Я в общем только очень недавно увидел, что такое бывает, и это офигительно. Рефакторишь код так, что уже давно прошел все пределы того, когда тесты, написанные по-старому, уже бы тысячу раз сломались "не по делу", а тесты тебе вместо того, чтобы мешать, помогают и подскзаывают, если ты действительно чего-то сломал. Но если не сломал, а просто поменял структуру кода - они не ломаются. Для меня это был полный снос башки.
Хороший пример того, как это делается, можно посмотреть в TDD курсе у J.B. Rainsberger (JBrains).
Конечно тут же встает вопрос, что такое юнит и что такое юнит тест, и в этот холивар я точно лезть не хочу : )
+1 к "Вот только все эти абсолюты к самому TDD имеют мало отношения, разве нет".
Владимир, вас, похоже, сильно травмировали, возможно не очень адекватные коллеги. Бывает.
Но у вас реально очень превратные представления о TDD, которые к реально практике отношения имеют очень мало от слова совсем.
100% покрытие вообще не является целью TDD. Это глупо и никому не нужно, потому что вот именно в этом случае придется писать совершенно нелепые тесты на сеттеры и геттеры и прочую ерунду. См. ссылку на то, как пишет тесты Кент Бек, которую давали выше. Никто из тех, кто работает по TDD из тех, с кем я лично общаюсь, не считает 100% покытие полезным, более того, в целом тестовое покрытие - метрика неоднозначная и проверяет только то, что бранч был вызван. Ничего про качество теста, вплоть тупо до наличия ассерта, оно не говорит.
Тесты как документация не является целью TDD! Совсем. Вы не с BDD перепутали?
Насчет того, что все примеры, которые приводят сторонники TDD, простые. Есть стримеры, которые показывают, как они работают над серьезными проектами и следуют TDD, есть видео, которые эти люди записывают и показывают. Из известных людей Uncle Bob, менее известных можно легко нагуглить при желании.
"Да, и что бы 2 раза не вставать замечу, что когда тесты покрывают код целиком и полностью, не оставляя живого места, любая попытка изменения функции или ее интерфейса приводит к дикому баттхёрту." - а вот это очень интересная тема. Дело вообще не в тестах, дело в моках. Есть две школы TDD, Чикагская и Лондонская. Чикаго - "мокисты", которые изолируют каждый класс и мокируют все зависимости. Лондонская школа избегает того, чтобы тесты знали, КАК метод делает то, что он делает - они проверяют только результат! И при таком подходе тесты - первейший инструмент рефакторинга, который помогает менять код, не ломая его. В этом собственно и основной смысл тестов.
"TDD позволяет сформировать хорошую архитектуру на ранних этапах, которую потом почти не придется менять (как и код), ибо TDD заставляет подумать до написания кода". TDD помогает писать код, который легко тестировать (юнит тестами, да). Задачи не менять её не стоит вообще! Совсем наоборот, при добавлении любой фичи - смотришь на то, а не поменялось ли чего более глобально, как эту фичу встроить; рефакторишь всегда! И тесты, если ты их сохранил гибкими и не знающими имлеметации того, что они тестируют, очень в этом помогают.
"Юнит тесты хорошо, а другие – плохо". Ну блин. Откуда вы это взяли? То, что есть тестовая пирамида, не означает, что не нужно ничего, кроме юнитов. И опять же, TDD тут мало при чем.
"Вот только легко тестируемый код не дает никаких гарантий качества получившегося приложения." - какие-то дает, но конечно не панацея. Если сначала не поговорили с клиентом или поговорили криво и сделали не то, то все в помойку.
В общем и целом, понятно, что вас сильно травмировали возможно не очень адекватные коллеги. Но вы реально написали статью, очень смутно представляя, что такое TDD на практике и зачем оно, и спорили всю статью сами с собой.
TDD очень обманчиво кажется простым и поэтому многие считают, что уже все про TDD поняли. На практике начиная пробовать TDD натыкаешься на кучу вопросов и проблем. Поэтому самый лучший вариант реально научиться и попрбовать - это поработать с кем-то, кто уже в этом реальный профи. Тогда и вопросы можно задать.
Одна из интересных неупомянутых проблем, например, это что делать, чтобы не писать в помойку. Обычная тема новичка - написать отличный класс, который потом окажется вообще не нужным, и его придется целиком выкинуть. Существуют возможности делать наброски даже по TDD, нащупывать первоначальную архетектуру - которую потом тридцать раз может быть поменяешь - которые помогают этого избежать.