Как и многие, я, как мог, сопротивлялся Test-Driven Development. Я не понимал, почему тесты нужно писать перед реализацией. Почему мы должны выворачивать разработку наизнанку и переворачивать естественный, как мне тогда казалось, процесс, с ног на голову.

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

И об этом мы поговорим в статье ниже.

Мы разберём страхи, останавливающие разработчика перед тем, чтобы начать, наконец, писать тесты. Выявим очевидные преимущества. Рассмотрим основные правила разработки через тестирование. И подкрепим всё это реальными примерами.

Какой ты сегодня разработчик? ツ
Какой ты сегодня разработчик? ツ

С чего начать?

Лично мой путь погружения в разработку через тестирование начался с книг Роберта Мартина. Первые упоминания о TDD появились в «Идеальном программисте», где Роберт Мартин упомянул такой подход вскользь. Ну а уж в «Идеальной работе» он отыгрался по полной, посвятив этому подходу чуть ли не половину книги.

Ещё более весомый вклад в продвижение разработки через тестирование внесла книга Кента Бека "Экстремальное программирование. Разработка через тестирование". Именно благодаря ей Кент Бек по праву считается основоположником этого подхода.

Начните с этих книг. Чёткий слог изложения Кента Бека и позитивный настрой Роберта Мартина воодушевят вас попробовать этот подход.

Начинать страшно

Страх первый. Придётся переучиваться программированию

Да, разработка через тестирование существенно меняет саму парадигму программирования, по сути, переворачивая его с ног на голову. Но на самом деле (внезапно) разработка через тестирование делает написание кода ещё более естественным, чем при обычном подходе.

Смотрите.

У нас есть техническое задание. Аналитик описал его в Jira, а может быть, в Confluence. Так или иначе, техническое задание написано человеческим языком и адресовано непосредственно разработчику. Но что, если бы мы могли адресовать техническое задание напрямую исполняемому коду? Чтобы техническое задание влияло напрямую на код и, к тому же, постоянно его валидировало?

Таким техническим заданием являются тесты.

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

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

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

Страх второй. Придётся постоянно писать и поддерживать кучу тестов

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

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

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

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

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

Та самая армия тестов
Та самая армия тестов

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

Страх третий. Посвятив себя написанию тестов, можно не уложиться в спринт

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

Написание одного теста лично у меня занимает в среднем 5 минут. Соответственно, написание десяти тестов займёт около 50 минут.

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

50 минут на один баг. Не много ли?
50 минут на один баг. Не много ли?

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

По какому алгоритму будет происходить лечение этого бага?

Тестировщику потребуется какое-то время, чтобы обработать баг. Ему нужно удостовериться, что это баг. Проверить, что он точно воспроизводится. Уточнить что-то у аналитика или разработчика. Оформить баг, приложив все данные. На это всё у тестировщика уйдёт минимум полчаса (а обычно - больше).

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

После этого, тестировщик берёт задачу в работу. Убеждается, что баг исправлен, и закрывает его. Ещё полчаса.

3 часа на баг, который вырвался на стенд
3 часа на баг, который вырвался на стенд

Итого, при самых скромных подсчётах, лечение одного бага занимает около 3 часов. А написание 10 тестов заняло бы 50 минут.

Не спрашивайте, где взять время. Возьмите его у себя. Напишите тесты.

Но каковы преимущества такого подхода?

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

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

Разберём основные преимущества разработки через тестирование.

Смелость

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

Живая документация

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

Архитектура

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

Снижение плотности дефектов

Результатом первых трёх преимуществ является преимущество четвёртое: множество компаний, самых разных, сообщают о снижении количества дефектов в 2, 5, 10 и более раз. Мы, как профессионалы, не можем игнорировать такие показатели.

Три закона Test-Driven Development

Итак, что же такое Test-Driven Development? Подход опирается на три закона, которые и определяют шаги разработки.

Закон первый. Сначала тест, потом функциональность.

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

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

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

Закон второй. Один тест проверяет только одну функцию.

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

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

Закон третий. Функция должна обеспечить только прохождение своего теста.

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

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

Всё просто.


Перейдём к практике

Для того, чтобы вышеописанные тезисы не пропали даром, их необходимо закрепить практикой. Что мы и сделаем. Мы создадим приложение и реализуем функции одного из сервисов при помощи Test-Driven Development.

Подготовка проекта

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

  1. Проектирование предметной области (бизнес-сущности).

  2. Реализация логики приложения через сервисы.

  3. Подключение к внешним интерфейсам через слой DAO.

Проектирование предметной области

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

data class Process(
    val id: UUID? = null,
    val state: ProcessState,
    val createdAt: OffsetDateTime? = null,
    val updatedAt: OffsetDateTime? = null
)

У Process будет минимальное количество полей: три служебных (id, createdAt, updatedAt) и одно бизнесовое (state). Служебные поля, поскольку они являются атрибутами хранилища данных, будут заполняться в репозитории. Бизнес-поле будет заполняться в приложении.

Проектирование сервисного слоя

Наш сервис будет содержать в себе стандартный набор CRUD-операций. Конечно же, на данном этапе это будет интерфейс.

interface ProcessService {

    fun save(process: Process): Process

    fun update(process: Process)

    fun get(id: UUID): Process

    fun delete(id: UUID)
}

Мы можем сразу создать ProcessServiceImpl, но и это не обязательно:

@Service
class ProcessServiceImpl(
    private val repository: ProcessRepository
) : ProcessService {

    override fun save(process: Process): Process {
        TODO("Not yet implemented")
    }

    override fun update(process: Process) {
        TODO("Not yet implemented")
    }

    override fun get(id: UUID): Process {
        TODO("Not yet implemented")
    }

    override fun delete(id: UUID) {
        TODO("Not yet implemented")
    }
}

Этого достаточно, чтобы приступить к разработке через тестирование.

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

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

Если бы мы начали с ProcessVersion, то для создания этой сущности нам бы пришлось предварительно создать Process, и мы всё равно пришли бы к реализации Process.

Этапы реализации созависимых сервисов тоже описаны в моей статье.

Мы начнём разработку с теста на функцию save. После прохождения этого теста, у нас будет реализована функциональность, позволяющая сохранять наш процесс где бы то ни было. Это позволит нам написать тесты на функции get, update и delete, требующие уже сохранённый в базе объект.

Жизненный цикл исполнения теста

Любой тест, явно или неявно, должен содержать в себе 4 этапа:

  1. Подготовка окружения.

  2. Вызов тестируемой функции.

  3. Проверка результата.

  4. Очистка данных.

Этап первый. Подготовка окружения

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

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

Генератор пригодится везде, где только можно
Генератор пригодится везде, где только можно

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

@Component
class ProcessGenerator {

    fun generate(): Process = Process(
        state = ProcessState.entries.random()
    )
}

В нашем примере, при генерации процесса нам потребуется проинициализировать поле state случайным значением.

По итогам подготовки, наш тест будет выглядеть так:

@Test
fun save() {
    //prepare
    val process = processGenerator.generate()
}

Перейдём ко второму этапу.

Этап второй. Вызов тестируемой функции

Здесь мы просто вызываем тестируемую функцию:

@Test
fun save() {
    //prepare
    val process = processGenerator.generate()
    //when
    processService.save(process)
}

Этап 3. Проверка результата

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

@Test
fun save() {
    //prepare
    val process = processGenerator.generate()
    //when
    processService.save(process)
        .also { saved ->
            //then
            assertNotNull(saved.id)
            assertNotNull(saved.createdAt)
            assertEquals(process.state, saved.state)
        }
}

Этап 4 мы реализуем централизованно чуть позже.

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

Принцип «Красный — Зелёный — Рефакторинг»

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

Этап «Красный»

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

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

Этап «Зелёный»

После того, как наш тест упал, мы переходим в пакет main и пишем реализацию. После этого наш тест перестаёт падать и становится «зелёным».

Этап «Рефакторинг»

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

Итак, наш тест упал. Перейдём в пакет main и посмотрим, что можно сделать, чтобы его починить.

Реализация тестируемой функции

Мы переходим в ProcessServiceImpl и видим такую картину:

override fun save(process: Process): Process {
    TODO("Not yet implemented")
}

Для того, чтобы наш тест стал «зелёным», необходимо, чтобы функция save возвращала Process с заполненными id и createdAt. Для этого нужно передать process в репозиторий и получить оттуда сохранённый объект.

Давайте напишем такую реализацию:

override fun save(process: Process): Process = repository.insert(process)

Но есть проблема. У нас нет никакого репозитория, и уж тем более, базы данных, и мы даже не знаем, какими они будут. Ведь такие вещи Роберт Мартин рекомендует оставлять на потом. А нужны они нам уже сейчас. Как быть?

В такой ситуации уместно будет воспользоваться mock-репозиторием и mock-хранилищем. Репозиторий мы разместим сразу в пакете test, потому что в реальной разработке он нам никогда не потребуется. Хранилищем будет стандартная хэш-таблица, идентификатором в качестве ключа и объектом в качестве значения.

Mock-реализации спасут нас на этапе тестового покрытия сервисов
Mock-реализации спасут нас на этапе тестового покрытия сервисов

Выглядеть такой репозиторий будет так:

@Component
class ProcessRepositoryMock(
    private val processStorageMock: MutableMap<UUID, Process>
) : ProcessRepository {

    override fun insert(process: Process): Process =
        process.copy(id = UUID.randomUUID(), createdAt = OffsetDateTime.now())
            .also { processStorageMock[it.id!!] = it }
}

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

Запускаем наш тест. Тест проходит. Ура.

Третий шаг реализации теста — «Проверка результата» — происходит сам собой. Но мы не реализовали четвёртый шаг — «Очистку данных».

Этап 4. Очистка данных

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

@Autowired
private lateinit var processStorageMock: MutableMap<UUID, Process>

@AfterEach
fun cleanUp() {
    processStorageMock.clear()
}

Теперь после каждого теста наша mock-база данных будет очищаться.

Что мы сделали?

Мы написали один маленький тест и сделали его «Зелёным». Мы написали очень важный для тестирования генератор данных, а также, mock‑репозиторий.

Но произошло ещё кое‑что действительно важное, на чём мы не акцентировали внимание. Попутно, мимоходом, мы написали реализацию функции ProcessService.save. И все остальные функции так же, попутно, будут реализовываться. Мы лишь будем писать тесты и делать их «Зелёными».

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

Это и есть разработка через тестирование.


Заключительные тезисы

Mock-компоненты

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

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

Генераторы

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

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


По традиции, выкладываю код с примерами:

https://github.com/promoscow/tdd-habr

Обратите внимание, что в git-логе есть 4 коммита, по количеству написанных тестов:

  • Шаг 1. Пишем тест на функцию save() + генератор + mock-репозиторий + mock-хранилище.

  • Шаг 2. Добавляем тест на функцию get().

  • Шаг 3. Добавляем тест на функцию update().

  • Шаг 4. Добавляем тест на функцию delete().

Откатиться на любой коммит можно через Reset Current Branch To Here... -> Hard.

Если некогда читать...

...у меня есть видео с конференции IT Community Day, которая прошла 12 октября 2024 года в Казани. Практически полный пересказ данной статьи с live-кодингом.

Запись доклада также есть и на YouTube.

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

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


  1. fo_otman
    15.11.2024 07:02

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


    1. qeeveex
      15.11.2024 07:02

      В итоге тратит х10 времени на отладку и проверку функционала на стейдже и проде. QA не смогут проверить всё, так как e2e и интеграционные физически не смогут покрыть все аспекты, поэтому unit тесты их дополняют.

      Если бизнес готов тратить деньги на баги и время на их исправление - пожалуйста.

      Я как профессионал буду отстаивать время на написание тестов. В случае проблем и потери денег придут в первую очередь ко мне.


      1. segment
        15.11.2024 07:02

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


        1. k4ir05
          15.11.2024 07:02

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


      1. sshikov
        15.11.2024 07:02

        Если бизнес готов тратить деньги на баги и время на их исправление - пожалуйста.

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

        В итоге тратит х10 времени на отладку и проверку

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

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

        В общем, ваша позиция имеет право на жизнь, в определенных условиях наверное совершенно правильная - но не универсальная.


        1. qeeveex
          15.11.2024 07:02

          А кто говорит что unit тесты замедляют разработку?

          Благодаря им происходит более быстрое внедрение новых фич. В моменте да, написание отнимает 50% времени, а то и больше, но нейросети сильно облегчили их написание. Главное накидать рабочий пример, а дальше они докидают кейсов.


          1. sshikov
            15.11.2024 07:02

            А кто говорит что unit тесты замедляют разработку?

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

            Даже если их делают нейросети - это все равно ваше время.

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

            накидать рабочий пример, а дальше они докидают кейсов.

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


            1. viordash
              15.11.2024 07:02

              SQL запрос, SELECT для ясности. Это настолько типовая функция приложения, что типичнее некуда.

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

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


              1. viordash
                15.11.2024 07:02

                И затраты на написание кода юнит-тестов иногда меньше, чем затраты времени на разделение\изоляцию рабочего кода на отдельные части, которые можно протестировать.


                1. sshikov
                  15.11.2024 07:02

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


              1. sshikov
                15.11.2024 07:02

                генерироваться в какой-то функции, и вот именно ее вам и нужно тестировать

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

                Но при выполнении у вас даже в простейшем случае два варианта - успешно и нет. Идем дальше - успешно это 0 строк ответа, 1 или много? Оно соответствует ожиданиям? Если неуспешно - то какой код ошибки вернула нам СУБД? Ну и на этом примерно все - как только нам это становится интересно, и мы пытаемся вот это протестировать, мы быстро понимаем, что API или контракт, как ни назовите, СУБД, настолько сложен, что писать под него юнит тесты ну очень муторно. Ну в принципе, потому, что СУБД как правило сильно сложнее нашего типичного прикладного продукта. Т.е. тестировать интеграцию с продуктом, который сложнее нашего, и пытаться это делать юнит тестами - по моему опыту довольно бессмысленно.

                замокать функции модуля\коннекшна базы данных

                А вы попробуйте как-нибудь на досуге :) Этот модуль в моем случае называется JDBC. И он, скажем прямо, уже как правило сложнее того кода, что пишет большинство миддлов по работе. Я пробовал его мокать/наследоваться. Эта овчинка не стоит выделки.


      1. DenSigma
        15.11.2024 07:02

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

        Вопрос с юнит тестами состоит в очень простой формулировке: время на разработку (и в особенности, на изменения и подгонку тестов при рефакторингах) юнит тестов больше, чем время на дебаг у ответственного разработчика? Если больше, они не нужны и вредны. Если в команде разработчики ВООБЩЕ не прогоняют свой код и не дебажат перед пушем - вопросов нет, юнит тесты нужны.

        И кстати, про QA.

        1. Задачей QA не является нахождение ошибок в коде разработчика. Задачей QA является недопущение проникновения дряни в прод и обеспечение соответствия кода функциональным требованиям (причин множество, и первейшая причина - неправильное понимание требований разработчиком).

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

        3. Если нет службы качества в конторе, то никакое юнит-тестирование и Бек вам не поможет.

        4. QA с девочками-тестерами должны проверять ВСЁ! Absolutly. Именно на них лежит ответственность за непрохождение брака клиенту.


  1. Farongy
    15.11.2024 07:02

    Что-то я о пользе TDD в статье так и не увидел информации. 95% статьи о том что нужно писать тесты. Но вот зачем их писать перед кодом из статьи неясно.


    1. qweururu
      15.11.2024 07:02

      Один довод там всё же есть(который первый, на тему формализации задачи). Остальное да, относится к "есть тесты или нет", а не к "тесты вначале".


    1. DenSigma
      15.11.2024 07:02

      А причина банальна. После того, как класс написан и ПРОДЕБАЖЕН, юнит-тесты нахрен не нужны.


  1. acordell
    15.11.2024 07:02

    Соглашусь с высказыванием выше, что дело тут не в программистах, а в вечном и жестком лимите времени. Да, в каком-нибудь петпроекте хоть оппишись тестов, никто ведь не стоит над тобой злой тенью. А в реальных проектах тебя на каждом дейлике подгоняют. Ладно тесты... В долгоживущих проектах в какой-то момент наступает момент, когда просто жизненно необходимо уже уделать время на его рефакторинг. Оптимально хотя бы 20% от общей разработки. Так вот чтобы их выбить, каждый раз при планировании нужна грубая лесть и угрозы суицида. Устаешь от этого гораздо больше, чем от всего остального. Менеджмент живет в иных сферах и нифига он разработку не слушает. У него KPI  на другое завязан. А что будет через несколько лет? А через несколько лет он уйдет на другой место с зелененькой галочкой в послужном списке о достигнутой эффективности на участке.


    1. lelikPtz
      15.11.2024 07:02

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


  1. ruomserg
    15.11.2024 07:02

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

    Не вдаваясь в дискуссию - давайте просто посмотрим на другие отрасли, и с удивлением обнаружим, что НИКТО (ни в авиации, ни в медицине, ни где либо еще) - не пытается делать тестовые стенды под еще не готовое изделие!

    С моей точки зрения, сторонники TDD путают написание теста до (или в процессе) разработки с понятием "testability" - то есть со способностью системы быть легко протестированной. Понятно, что если вы уже написали тест до создания системы, то она вынуждена оказаться testable - но какой ценой ?!

    В более зрелых отраслях промышленности уже давно найдены пути обеспечения тестируемости системы БЕЗ создания тестовых стендов наперед! Да, на этапе проектирования и создания системы - обязательно надо думать о том, как мы будем это тестировать. Что подразумевает и разумное разделение интерфейсов, и IoC, и следование принципам SOLID, и много чего еще.

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

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


    1. AdrianoVisoccini
      15.11.2024 07:02

      не то чтобы я защищал TDD но как будто вот это заявление

      Не вдаваясь в дискуссию - давайте просто посмотрим на другие отрасли, и с удивлением обнаружим, что НИКТО (ни в авиации, ни в медицине, ни где либо еще) - не пытается делать тестовые стенды под еще не готовое изделие!

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

      почему же в авиации не делают тестовые стенды до самолета? Ну во первых аэродинамическая труба это не тестовый стенд который у вас есть до наличия самолета? Или даже 3д модели которые тестируют на компьютере даже до стадии прототипирования?
      ну давайте все это отбросим и задумаемся - а может быть причина, например, в цене такового стенда? Unit тест пишется как заявляет автор 5 минут, можете высчитать стоимость от своей ЗП за эти 5 минут. Аэродинамическая труба стоит куда дороже даже за 5 минут тестирования в ней, не то что её постройка. TDD как раз и существует лишь по тому что стоимость создания тестового стенда до создания тестируемого объекта нулевая, в отличии от любой другой отрасли.


      1. ruomserg
        15.11.2024 07:02

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

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

        С другой стороны, если дурак-конструктор сделает такую модель, что там физически нельзя сделать интерфейс со стендом, то конструктора сначала обсмеют в курилках, а потом он пойдет переделывать свое изделие. Потому что когда ты что-то проектируешь НАДО думать о том, как ты это будешь тестировать.

        В общем, еще раз - не надо путать обеспечение testability на этапе проектирования и изготовления, и фактического изготовления стенда вперед изделия...


        1. AdrianoVisoccini
          15.11.2024 07:02

          ладно, пожалуй стоит сформулировать мою мысль по другому

          "аналогия не является аргументом" - вот так наверное лучше. Нельзя сравнивать разработку самолета и разработку ПО и говорить - вот они так не делают и нам не нужно по этому. На стройке скрам не используют, сварщики не пользуются DDD, водителям такси не навязывают SOLID принципы управления автомобилем. В IT есть своя специфика и критиковать использование конкретного инструмента, ровно как и оправдывать таковое использование, нужно находясь в рамках IT и аргументы приводить соответствующие. "Представьте если бы ваша мама перед тем как варить борщ делала бы тестовый стенд для борща" это не аргумент, это эмоции

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


          1. ruomserg
            15.11.2024 07:02

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


            1. AdrianoVisoccini
              15.11.2024 07:02

              ещё раз повторюсь, что я не защищаю TDD, но все ещё не могу согласиться с тем что


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

              а почему не могут то? Они и есть радикально другие, так как работа идет с информацией - буквально абстрактным понятием, имеющем лишь некоторое физическое воплощение, от того не становящееся менее абстрактным. Следовательно и подходы к работе с этим абстрактным понятием могут отличаться от всех остальных инженерных дисциплин. Достаточно того, что в ИТ, в отличии от вышеназванных дисциплин буквально по каждому подходу/стандарту/практике есть горячий спор, сторонники и противники и доводы из разряда религии. Один КлинКод чего стоит - с одной стороны фанатичные клинкодеры которые готовы тратить лишнее время на эфемерную чистоту кода которую и измерить то нельзя, с другой стороны оптимизаторы с бенчмарками которые на деле могут показать как без клин кода то же самое работает в 20 раз быстрее. Ну и назовите мне хотя бы одну отрасль инженерии, где бы такое существовало. Да, локальны срачи есть много где, но так чтобы ПО КАЖДОМУ ВОПРОСУ - такое есть только в IT как мне кажется, и обсуловленно это именно абстрактностью информации и невозможности выработать один универсальный подход.

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


              1. ruomserg
                15.11.2024 07:02

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

                И да, наверное у меня более "железячное" отношение к ИТ, просто в силу того, что я его застал на более ранних стадиях. Одну и ту же задачу принципиально можно решить жестко соединив логические элементы паяльником, или прожигая перемычки физически в микросхеме, или делая это во флэш-памяти (ПЛИС), или записать программу в RAM для ЦП общего назначения... Поэтому я не вижу прямо вот принципиальных отличий нас от инженеров-электронщиков...


      1. ruomserg
        15.11.2024 07:02

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


        1. sshikov
          15.11.2024 07:02

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


    1. sshikov
      15.11.2024 07:02

      Не вдаваясь в дискуссию - давайте просто посмотрим на другие отрасли, и с удивлением обнаружим, что НИКТО (ни в авиации, ни в медицине, ни где либо еще) - не пытается делать тестовые стенды под еще не готовое изделие!

      Простите, но нет. Более того, я слышал например мнение, что советская ракета Н-1 не полетела (в отличие от Аполлона) в частности и потому, что не было денег на тестовые стенды например для двигателей, и их не удалось достаточно отработать до полета.

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


      1. ruomserg
        15.11.2024 07:02

        Первые серии двигателей Кузнецова для Н-1 - это как раз пример non-testable системы: двигатель одноразовый, после прожига выбрасывается. В "Ракетах и людях" Б.Чертока описывается процедура - прожигают из партии половину двигателей на стенде, и если они отрабатывают штатно, то остаток партии идет на сборку ракеты (в предположении что оставшиеся изготовлены из одних и тех же материалов по одному технологическому циклу - и, следовательно, тоже не откажут). По факту, не взлетело... Вторая серия двигателей была уже многоразовая и позволяла ставить на ракету двигатель прошедший стенд и переборку. Но было поздно...

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

        Я историей Н-1 на самом деле несколько раз дожимал людей от бизнеса выделять ресурсы на тестирование. Уж больно показательная! Но еще раз - это не про то, что надо тест писать вперед кода, а что при написании кода надо предусматривать что он должет быть testable (ну и иметь ресурс реализовать эту testability - то есть время и деньги на тестирование).


        1. sshikov
          15.11.2024 07:02

          Но при этом - никто в здавом уме и твердой памяти не сказал ни Королеву/Мишину, ни Кузнецову, что еще до создания двигателя надо сделать стенд для его прожига

          Знаете что самое смешное? Что мнение о том, что не хватило ресурсов на стенды было когда-то высказано именно Мишиным. Сильно позже, но такой вывод он сделал.


          1. ruomserg
            15.11.2024 07:02

            Ну, задним числом это в принципе все признали. Что отрабатывать пусками ракету класса Н-1 оказалось и запредельно дорого, и скорее всего по деньгам оказалось бы дороже чем если бы работать со стендами. Но перечитайте мой пост - я целиком за то, чтобы тестировать юнит-тестами, интеграционными, и компонентными. Ручное тестирование, с моей точки зрения, это как раз попытка отработать ракету запусками с площадки...

            Но в любом случае, нигде кроме программирования никто не пытается сделать сначала тестовый стенд, а потом изделие под него. Возьмем более близкую нам область - электронику. Да, разработчик обязан предусмотреть тестовые пятаки на плате, чтобы можно было к ним добраться через pogo-pins на стенде. Да, разработчик обязан предусмотреть JTAG. Да, разработчик обязан предусмотреть чтобы можно было отдельно проверить блок питания, а потом поставить перемычку и запитать всю схему (а не жечь ее при первом включении). Но сам тестовый JIG делается только после того, как утряслось расположение компонентов и рабочих разъемов, и сделали первый экземпляр. В ином случае - это же абсурд! У нас на плату компонент не встает, или надо дорожку переложить потому что выявилась проблема с электромагнитной совместимостью - а мы уже тестовый jig сделали, и теперь у нас не одна, а целых две проблемы... Я не спорю, что если есть время и деньги, можно хоть с первого дня проектирования написать все тесты - а потом по ходу разработки их переделывать. Но блин, никто в других инженерных дисциплинах так не делает! Не может быть чтобы все вокруг - дураки, а мы - самые умные...


            1. sshikov
              15.11.2024 07:02

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

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


              1. ruomserg
                15.11.2024 07:02

                Именно так! Нормальные люди стенды делают со сдвигом вправо относительно разработки изделия. А TDD в ее "чистом" понимании религиозными фанатиками - запрещает разработку изделия ДО того как вы сделали стенд для его тестирования. Что есть инженерный абсурд...

                Я пытался понять, что именно заставляет их упарываться в тестирование таким образом - и пришел к выводу, что отсутствие культуры тестирования (а я еще пару лет назад видел выходящих из технического вуза программистов, которые были уверены что тестирование - это "...запустить программу и повводить в нее разные данные") - приводит к тому, что программа проектируется и кодируется способом, который исключает модульное тестирование - то есть программируется каша. Соответственно, если мы начинаем требовать сначала написать тест, а потом писать код - то такую кашу уже не напишешь - потому что иначе не будет теста, а ты обязан его написать до того как напишешь сам код... Я допускаю, что этот подход может быть применен во время обучения, чтобы через руки до студентов дошло, как надо писать программы. Но требовать TDD в нормальной производственной среде - oh, my!..


                1. sshikov
                  15.11.2024 07:02

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

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


    1. k4ir05
      15.11.2024 07:02

      НИКТО (ни в авиации, ни в медицине, ни где либо еще) - не пытается делать тестовые стенды под еще не готовое изделие!

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

      Кажется, вы неверно понимаете TDD. В TDD написание тестов это способ детальней формализовать требования.


      1. ruomserg
        15.11.2024 07:02

        Вы серьезно ?! Требования к ПО описывают внешние по отношению к системе требования. А поклонники TDD требуют писать тесты ДО написания любого производственного кода. И это уже никак не требования к системе - потому что никто извне не заставляет вас членить систему именно таким образом или выбирать именно такие алгоритмы для реализации. Это вы прибиваете намертво гвоздями к столу артефакты реализации своей системы. И если вам не повезло, и вы не угадали сразу с тем как надо было делать - то любой рефакторинг в этой прибитой к столу реализации - выливается в пот, кровь, время и деньги...


        1. k4ir05
          15.11.2024 07:02

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

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


          1. ruomserg
            15.11.2024 07:02

            Но ведь TDD - это тест Driven (!) разработка, не ? По канонам этой религии, нельзя садиться и писать метод внутри класса, не написав предварительно тест. И если вы будете писать тесты ad-hoc, то у вас будет тест на метод a(), потом тест на метод b() который вызывает "a", потом на c() который вызывает "a" и "b", и так далее... Если же вы хотите избежать дублирования тестового функционала - то, извините, вам придется хотя бы в голове написать тесты, чтобы решить какие из них и в какой части следует сократить...

            Собственно, тесты здорового человека тем и отличаются от тестов курильщика, что пишутся ПОСЛЕ того как функционал (или его часть) уже разработаны. И вы, имея перед глазами уже существующую структуру - решаете, что именно в рамках имеющегося бюджета и денег вы закроете тестами. То есть, соблюдается четкая иерархия ценностей как во всех нормальных инженерных дисциплинах: разработка драйвится требованиями, тесты драйвятся техническими решениями разработки.


      1. 0xBAB10
        15.11.2024 07:02

        единственный способ формализовать требования - это (внезапно!) формальная спецификация

        по определению, тест это поиск контрпримера к конкретному юзкейсу в конкретном контексте. фаззинг и property-based будут чуть-чуть, на полшишечки лучше, чем полностью врукопашную написанный тест, но глобально тесты даже с покрытием 100% ничего не гарантируют


      1. DenSigma
        15.11.2024 07:02

        Если бы тесты писались постановщикам - да, это аргумент того, что это формализация требований. Но тесты пишутся разработчиками. Как они поймут проект- это одному богу известно, а они пишут тесты именно на основании того, как ОНИ поняли проект, как поняли проект РАЗРАБОТЧИКИ, а не проектанты.


  1. LyuMih
    15.11.2024 07:02

    С точки зрения фронтенда мне не нравится, что тестирование не встроено в платформу. Все возможные jest/vitest/mochat cypress/playwright и т.п..
    Каждый проект нужно настраивать по своему, что-то по дороге может сломаться, или не запуститься как надо на windows/macbock.

    Потом в один момент тесты становятся гигантскими, занимают на ci/cd больше времени, чем сборка самого проекта.
    Локально без плагинов в режиме watch не запустишь. А если запустишь, то оно сожрёт половину процессорных мощностей.

    И получается, что тестировать tdd имеет смысл только очень маленькую часть (логики). Самое важное - не тестировать то, что не надо. Всякие render функции и т.п.


  1. 0xBAB10
    15.11.2024 07:02

    удивительно с каким упорством автор избегает тему Хрупкости тестов

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

    вот про что нужно писать статьи.

    а пока вы пели оды Мартинам и Бекам они уже давно открестились от ТДД и сами признали ТДД несостоятельным