Первые шесть лет своей карьеры я фрилансил и участвовал в начальных этапах жизни мелких стартапов. В этих проектах не было тестов… Реально, ни единого. В этих условиях ты обязан реализовать фичи на вчера. Поскольку требования рынка постоянно меняются, тесты устаревают ещё до того, как ты их заканчиваешь. И даже эти тесты можно создать только, если ты уверен в том, что именно ты хочешь создать, а это не всегда так. Занимаясь R&D ты вполне можешь не знать каков должен быть конечный результат. И даже достигая определённых успехов, ты не можешь быть уверен, что завтра рынок (а с ним и требования) не изменятся. В целом, существуют бизнес причины для экономии времени на тестировании. Согласен, наша отрасль — это не только стартапы.
Около двух лет назад я устроился в достаточно большую аутсорсинг компанию, которая обслуживает клиентов любых размеров.
Во время разговоров на кухне/курилке, я обнаружил, что практически все согласны с тем, что юнит-тестирование и TDD это своего рода best practice. Но во всех проектах этой компании, в которых я участвовал, не было тестов. И нет, не я принимал такое решение. Конечно же, у нас есть проекты с отличным покрытием тестами, но они ещё и довольно сильно бюрократизированы. Так в чём же проблема?
Почему все соглашаются, что TDD это хорошо, но никто не хочет его применять?
Может TDD ошибочно? – Нет!
Возможно, в нём нет никакой выгоды для бизнеса? – И опять, нет!
Может просто разработчики ленивы? – Да! Но это не причина.
Проблема в самих тестах!
Я понимаю, что звучит это странно, но я попытаюсь это доказать.  Исходя из этого исследования наименьшая общая удовлетворённость во всей экосистеме принадлежит именно инструментам для тестирования. Так было в 2016 и 2017 годах. Я не нашёл более ранних исследований, но это уже не очень важно. В 2008 году вышел один из первых JS фреймворков для тестирования (QUnit).
В 2010 появился Jasmine.
В 2011 – Mocha.
Первый релиз Jest, который я нашёл, был в 2014.  Для сравнения.
В 2010 зарелизился angular.js.
Ember появился в 2011.
React — 2013.
И так далее…  За этот же период времени мы увидели взлёт и падение grunt, потом gulp, после чего осознали всю мощь npm scripts и вышел в свет стабильный релиз webpack.  Всё поменялось за последние 10 лет. Всё кроме тестирования. Давайте проверим ваши знания. Что это за библиотеки/фреймворки?  1: 2:  3:  Ответы:  Хорошо. Я уверен, что все ваши ответы были верны. Но что на счёт этих фреймворков для тестирования?
1:  2:  3:  4:  Ответы:  Вы могли угадать некоторые из них, но, в целом, они все очень похожи. Заметьте, что даже при смене языка, мало что меняется. У нас есть, как минимум, 8 лет опыта юнит-тестирования в мире JavaScript'а.
Но мы ведь просто адаптировали уже существующее на тот момент. Юнит-тестирование, как мы его знаем, появилось намного раньше. Если взять релиз Test Anything Protocol (1987) как точку отсчёта, то мы используем текущие подходы дольше, чем я живу. TDD ненамного моложе, если не старше. Всё это приводит нас к тому, что мы уже можем объективно оценить все плюсы и минусы. Давайте вспомним, что такое TDD.  Но что это нам даёт? Это правда только частично. TDD как практика была "переизобретена" Кентом Беком в 1999 году, в то время как Agile Manifesto был принят только 2 года спустя (в 2001). Я должен это подчеркнуть, что бы вы поняли, что TDD родился в "Золотой век" каскадной модели и этот факт определяет наиболее благоприятные условия и процессы, для которых он и был спроектирован. Очевидно, что TDD будет лучше всего работать именно в таких условиях. Так что, если вы работаете в проекте, где: Вы можете создавать тесты, как формализацию требований.
Но что бы использовать существующие тесты таким же образом, необходимо выполнение и следующих пунктов тоже: Так что "Тесты — это формализованные требования" — правда только тогда, когда эти требования существуют до начала самой разработки, как в "модели Водопад" или проектах NASA, где "клиенты" это ученые и инженеры. И опять это правда только частично.
TDD поощряет модульность, что необходимо, но недостаточно для хорошей архитектуры. Качество архитектуры зависит от разработчиков. Опытные разработчики способны создавать отличный код, несмотря на использование или неиспользование юнит-тестирования.
С другой стороны, слабые разработчики будут создавать низкокачественный код, покрытый низкокачественными тестами, потому что создание хороших тестов — это своего рода искусство, как и само программирование. Этот тест никак не продвинет вас на пути к хорошему дизайну системы: Потому, что он ничего не тестирует. Но и этот тест не сделает дизайн вашей системы лучше: Наибольшая проблема этого теста — это изначальная кодовая база, но даже в таком случае его можно было существенно улучшить, даже без рефакторинга уже работающего проекта. Вообще, влияние TDD на итоговую архитектуру приблизительно на том же уровне, что и влияние выбранной библиотеки/фреймворка, если не меньше (например, Nest, RxJs и MobX, по моему личному мнению, влияют существенно сильнее). Но ни TDD, ни фреймворки не спасут от плохого кода и неудачных архитектурных решений. А это уже зависит от многих факторов...
Давайте предположим, что: Даже в этом случае, вам необходимо сначала инвестировать время и усилия, что удлинит начальную фазу разработки и только спустя какое-то время вы получите выгоду, сократив необходимое время на исправление ошибок и поддержку продукта.
Конечно, второе может быть больше чем стартовая инвестиция и в этом случае выгода от TDD очевидна.
Так же в некоторых случаях вы сможете сэкономить время и на внедрении новой функциональности, поскольку тесты будут сразу выявлять непреднамеренные изменения.
Но в реальном мире, который очень динамичен, требования могут измениться и то, что было корректным поведением раньше, станет некорректным. В этом случае вам необходимо переписать тесты в связи с новыми реалиями. И, очевидно, приложить новые усилия, которые не окупятся сразу же. Вы даже можете попасть в цикл подобного типа:  Ладно, этот цикл противоречит принципам TDD. Но следующий уже нет:  Попробуйте найти в них значимые различия. Нет. Они хороши в этом, но однозначно не лучшие. Давайте взглянем на документацию angular:  Или react:  Как вы думаете, что в них общего? — Они обе построены на примерах кода. И даже более того. Все эти примеры можно легко запустить (angular использует StackBlitz, а react — CodePen), так что вы можете увидеть, что оно даёт на выходе и что произойдёт, если вы что-то измените.
Конечно же, там так же есть и простой текст, но это как комментарии в коде — они вам нужны только если вы что-то не поняли из самого кода. Исполняемые примеры кода — вот лучшая документация! Тесты близки к этому, но недостаточно. Это небольшой кусочек из реального теста в react. Мы можем выделить примеры кода из него: Всё остальное это вручную написанный инфраструктурный код. Давайте будем честными, пример теста выше намного менее читабельный чем настоящая документация. И проблема не в этом конкретном тесте — я уверен, что ребят из facebook знают, как писать хороший код и хорошие тесты :) Весь этот мусор из инструментов тестирования и assertion библиотек, как it, describe, test, to.be.true просто захламляет ваши тесты. Но стоит заметить, что тесты всё ещё вполне пригодны для использования в качестве документации. У них действительно ниже вероятность быть устаревшими, а наше сознание просто выкидывает лишнее, когда мы их читаем. Если мы попробуем визуализировать то, во что превращается тест в нашей голове, то это будет выглядеть приблизительно так:  Как вы видите, это уже намного ближе к реальной доке, чем изначальный тест. Значит TDD всё-таки ошибочно? — Нет, TDD не ошибочно.
Оно указывает правильно направление и поднимает важные вопросы. Мы просто должны переосмыслить и изменить способ его применения. Не воспринимайте TDD как "серебряную пулю".
Не воспринимайте его даже как процесс по типу Agile, например.
Вместо этого сфокусируйтесь на его реальных сильных сторонах: Думайте о юнит-тестировании как о инструменте разработчика. Как линтер или компилятор, например. Когда-нибудь это станет реальностью и для юнит-тестирования. Когда необходимые для TDD усилия будут на уровне использования тайпчекера или бандлера. Но до этого момента, просто минимизируйте свои затраты, создавая тесты максимально похожими на исполняемые примеры и используйте их как текущий baseline состояния вашего проекта. Я понимаю, что это будет сложно, особенно учитывая тот факт, что большинство популярных инструментов спроектированы для других целей. Правда, я создал один такой, беря во внимание все вышеописанные проблемы. Он называется  Базовая концепция очень проста. Пишите код: И просто используйте его в вашем тесте: Потом выполняете команду baset test и получаете временный baseline: Если значения верны, выполняете baset accept и коммитите созданный baseline в ваш репозиторий. Все последующие прогонки тестов буду сравнивать существующий baseline со значениями, экспортированными из ваших тестов. Если они отличаются, тест провален, иначе — пройден.
Если требования изменились, просто измените код, прогоните тесты и примите новый baseline. Этот инструмент всё ещё оберегает вас от непреднамеренных изменений, при этом требует минимальных усилий. Всё что вам нужно, это просто написать исполняемый пример кода, который, к тому же, является основой хорошей документации. Использование с react. Вот этот тест: создаст такой .md файл как baseline: exports.value: Или с pixi.js: Этот тест создаст такой baseline: exports.sprite:  Я обязан сказать, что этот инструмент ещё на очень ранней стадии разработки и впереди ещё очень много нововведений, например: Только около 40% от запланированного было реализовано. Но вся базовая функциональность уже работает, так что можете попробовать поиграться с ней. Может быть вам даже понравится, кто знает?