Перед TDD, я написал код, который должен работать, проверил его вручную в моем браузере, чтобы увидеть, почему он не работает, исправил его, а затем написал тесты. С TDD я пишу функциональные декларации и тесты, которые описывают, как должен работать код, и определяю, какие критерии заставят меня поверить, что код работает. Только тогда я могу заставить его работать. Определение «работы» уже не является двусмысленным, у меня есть стандарт судить о себе, что может быть выполнено программно. Запихнув мои критерии тестирования в коде, человек, выполняя обзор кода, может обратиться, если критерии тестирования верны или нет. Эта последняя часть позволяет избежать неоднозначного заявления «работает для меня» между членами группы и последующей деятельности, чтобы выяснить, что отличается – тестовая среда или критерии тестирования. С ручным КК, если вы документируете каждый этап тестирования, вы просто не имеете этого.
В этой статье я собираюсь пройти через с помощью примеров из моего плагина Caldera формы использования фпунита для написания TDD тянуть запросы.
Это не статья о том, как сделать тестирование в разработке WordPress. Я писал о тестировании единицы PHP, тестировании интеграции PHP,тестировании единицы JavaScript, тестировании интеграции JavaScript для крутящего момента раньше. Эта должность основана на моем опыте принятия TDD для нашего плагина Caldera формы. Я предпочитаю TDD и думаю, что это быстрее, особенно в разработке JavaScript, чем вручную тестирование с браузером. Когда все сделано правильно, это делает код более пригодным для обслуживания.
Быстрое введение в TDD
Если вы новичок в TDD, вам может быть легче думать о нем, как тест-первых. Вот шаги в легко следовать список:
- Напишите достаточное количество функций, необходимых для описания их в тестах.
- Напишите неудачные тесты.
- Сделать тесты проходят.
- Обзор кода.
При использовании TDD, вы должны сделать и нажать коммиты, которые не работают. Нажатие на мастер или вашу ветвь развития до прохождения тестов будет означать, что каждое изменение нарушает основную ветку, что является проблемой. Вот почему поток git имеет смысл. Вы открываете новую ветку, вносите постепенные изменения и объединяете изменения только при прохождении автоматизированных проверок, таких как тесты, ворсы, сканирование качества кода и пропуск обзора человеческого кода.
Давайте поговорим о надуманный пример, так что это просто, а затем посмотреть на реальный пример.
Допустим, ваше требование заключалось в создании объекта для добавления двух чисел. Во-первых, я хотел бы написать функцию без тела:
Затем я хотел бы добавить два теста для обеспечения того, чтобы утверждали правила математики в настоящее время следуют:
Тогда я хотел бы изменить свою первоначальную функцию — в отдельном коммите — на самом деле работать:
Причина, по которой не завершенные тесты задавлены до того, как код будет работать, объясняется двумя причинами. Во-первых, коммиты должны делать одно только тогда, когда это возможно. Во-вторых, возможно, что после того, как тесты пройдут, вы или код человека, просматривающий изменение, можете решить, что тесты были правильными, но реализация не была выполнена. Если это произойдет, вы можете вернуть коммит с реализацией, не теряя тестов или делать любитель git, а затем начать все сначала.
TDD окупается позже
Этот код тестирует JavaScript прямо сейчас. Но наличие хорошо проверенной функции на месте позволяет безопасно итерировать на нем. Предположим, что вам нужно добавить третий аргумент, чтобы опционально округлить результат к определенному числу десятичных знаков. Сначала измените подпись функции:
Я сделал это необязательный аргумент для поддержания обратной совместимости. Чтобы убедиться, что мое предположение правильно, я не собираюсь менять существующие тесты. Они доказывают, что обратная совместимость была сохранена. Изменение существующих тестов плохо пахнет и является признаком того, что ваши тесты являются слишком жесткими.
Вместо этого я хотел бы добавить еще два теста:
Внедряя тесты на каждом этапе, у меня есть страховка, что мои улучшения являются фактическими улучшениями, а не вызывает новых дефектов. Когда вам придется изменить существующий код в будущем, время, вложено ею в тесты, окупается.
Сколько тестирования покрытие вам нужно?
Это зависит от того, что вы строите. Самые ортодоксальные правила TDD я могу найти приходят от дяди Боба:
- Запрещается писать какой-либо производственный код, если только он не должен сделать пропуск теста единицы.
- Вы не можете написать больше модульного теста, чем этого достаточно, чтобы потерпеть неудачу, а сбои компиляции являются сбоями.
- Вам не разрешается писать больше производственного кода, чем этого достаточно для прохождения одного неудачного модульного теста.
Это стандарт, который является очень жестким и может легко привести к тестам, которые делают изменение базы кода труднее. Rember, цель состоит в том, чтобы увеличить, а не уменьшить скорость развития.
Кент C. Доббс — инженер PayPal, который также является автором курса по тестированию JavaScript приложений — имеет большой пост на эту тему. Он утверждает, что в этой должности, что «вы получите уменьшение возвращается на тесты, как охват увеличивается гораздо выше 70%». Я не люблю это заявление, но он более опытный, чем я, по многим. Он отмечает, что его проекты с открытым исходным кодом имеют 100% охват, потому что они являются «инструментами, которые многоразовые в различных ситуациях (поломка может привести к серьезной проблеме во многих потребляющих проектов)» Который звучит как правило, которое будет применяться к WordPress Плагин. Он также пишет, что шис OSS проекта «относительно легко получить 100% код покрытия на любом случае». Что не звучит как много плагинов WordPress.
Лично мое правило больше охвата, чем мы в настоящее время. Отсутствие тестов является технической задолженности, которая приходит из-за позже. Если написание тестов теперь занимает больше времени, это того стоит. Для совершенно нового кода он заставляет вас писать код с помощью тестируемых шаблонов. Необходимость рефакторинга кода, так что это testable первый боль иногда.
Изолированное модульное тестирование в WordPress не просто. Часто автоматизированные тесты uI с использованием Cypress.io или Ghost Inspector служили мне лучше. Я могу быстро покрыть много функциональных возможностей, не беспокоясь о том, что код на самом деле не тестируется.
Добавление функции с TDD
Я хотел бы пройти через пример TDD тянуть запрос я сделал Кальдера формы. В этом случае это была новая функция — добавление параметра для максимального размера загрузки файла. Одна часть TDD, что мне нравится это заставляет вас выяснить, какие новые функции вам нужно, прежде чем писать их. Я не знаю, как вы, но я написал много кода, что занимает много времени, чтобы получить работу только понять, что я не нуждался в нем. TDD заставляет меня продумать свои планы, прежде чем двигаться вперед.
Вот запрос на тягу на Github, если вы хотите прочитать его: https://github.com/CalderaWP/Caldera-Forms/pull/2823
Я должен отметить, что этот PR странно, потому что мы должны были объединить несколько незавершенных ветвей из связанных изменений вместе. Принятие TDD является грязным, и я утверждаю, не совершенное соблюдение своих законов.
Написание неудачных тестов
Иногда это трудно сделать, что все сразу. Например, в этом случае мне нужно было разработать некоторые полезные методы для чтения параметров поля и проверки размера файла, и мне нужно было интегрировать эти утилиты в существующий код. Я решил сделать это в два этапа. Я получил утилиты методы работы и испытания, а затем я переехал использовать эти новые методы.
Вот мой первый коммит: https://github.com/CalderaWP/Caldera-Forms/pull/2823/commits/d878e9d501af6aae92d72e45340a185dea1e9c69
Если вы посмотрите на код, я добавил два утилиты метода в класс, и не дал им функции тела:
Вот и все в этом коммите для кода, который я разрабатываю. Я также провел тесты, которые демонстрируют, как эти функции должны работать:
Эти тесты показывают, как новые методы должны работать. В внеочередных комментариях объясняется, почему делается каждое утверждение. Этот процесс заставил меня задуматься о том, как настройки, которые на данный момент не имели uI, должны быть структурированы и как я буду позже их использовать.
Мое общее правило заключается в том, что коммит должен делать только одно. Этот коммит добавляет новые методы и неудачные тесты. Слово «и» в предыдущем предложении показывает, что я нарушил это правило. Я, наверное, должен был сделать два коммитов. Что еще более важно, я хочу отметить, что я потратил много времени, работая через логику того, что для тестирования и Есть много предварительного обязательства изменений там.
Прохождение тестов
В прошлом, когда я не использовал TDD, я должен был бы проверить это, добавив опцию uI, создав форму с этой опцией, а затем представить форму с файлом и посмотреть, если мой код работал как ожидалось. Использование xDebug для прохождения кода и изучения результатов работ помогает процессу, но это так медленно. Кроме того, как только он работает, нет никакого способа узнать, если что-нибудь еще перерывы, что функция позже.
Вот почему я считаю, TDD быстрее — когда это возможно — и экономит время и беспокойство в будущем. Запуск всего набора тестов между каждым коммитом сделает процесс очень медленным. Для тестирования JavaScript я использую Jest в качестве тестового раннера, и его можно легко настроить для выполнения только измененных тестов. Это приводит к стратегии, где я пишу неудачные тесты, получить все тесты, связанные с изменениями, чтобы пройти, а затем я говорю ему, чтобы запустить все мои тесты, чтобы убедиться, что ничего неожиданного не произошло.
Вот статья о том, как сделать что-то подобное с phpunit. Мое личное решение заключается в использовании «теперь» группы аннотации в моем docblocks. В phpunit можно использовать @group для группы тестов по функциям. После этого вы можете побежать только испытания в той группе с флагом группы в cli phpunit. В Caldera Forms у нас есть композиторский сценарий для запуска @now тестов.
С тестами на месте, я смог начать работать над моими двумя новыми методами и запустить только два теста каждый раз и посмотреть, почему каждый тест не удается. В процессе я видел как ошибки, предупреждения и уведомления PHP, так и сбои в тестировании. Я получил довольно надоеденным на себе на один этап для кода, который должен был работать, и я понятия не имел, почему нет, но по крайней мере у меня было доказательство, что я не был сумасшедшим.
Я должен также отметить, что причина, по которой я написал два теста с несколькими утверждениями, что они терпят неудачу быстрее, что путь. Если бы у меня было одно утверждение на тест, как это часто рекомендуется, и, как правило, хорошая идея, я бы видел 10 или около того не удается в тесте. Это намного сложнее разобраться. Организация одного теста с одним утверждением за другим помогает решить одну проблему за один раз.
Фактические функции, которые я написал, выглядят довольно просто:
Мои тесты охватывают несколько различных ситуаций, которые легко игнорировать. Например, что происходит, если параметр не существует. Условная логика, основанная на содержании ассоциативного массива, сложна в php, потому что индексы могут отсутствовать, значения могут быть представлены с различными типами масштабов — что делать, если целые 1 или строки ‘1’ или ‘истинный’ используется вместо boolean правда?
Я определенно думал, это просто, мне не нужны тесты во время работы над этими методами. Кроме того, моя первая попытка не работает, и я знаю, что, поскольку мои тесты не удалось и не таким образом, что помогло мне понять, почему.
Перемещение за пределы изоляции
Тесты до сих пор были технически интеграционные тесты, потому что окружающая среда требует WordPress на работу, и я не думаю, что использование макетов для модульных тестов стоит боли. Остальные тесты были в основном фактические интеграционные тесты.
Для полей файлов Caldera Forms у нас есть отдельная конечная точка для загрузки файла. В этом случае мне не нужно было прикасаться к обработчику этой конечных точек, потому что я не смешивал эту логику с обработчиком API. Ответственность обработчика API является взаимодействие WordPress REST API и отдельного класса «UploadHandler», который делает загрузку, используя данные, передаваемые с REST API.
Это означало, что я только должен был внести изменения в моем «UploadHandler». Этот класс менялся. Внесенное изменение необходимо для обеспечения соблюдения предела размера файла. Бизнес-логика была в другом месте. Мне просто нужно, чтобы убедиться, что если я дал ему слишком большой файл. Мне нужно было убедиться, что при правильном файле размера он работал так же, и он бросил исключение, когда файл был слишком большим. Вот три новых теста:
Первый тест — testKnowsFileTooLarge() — не делает все перестановки тестов, которые я имел для утилиты метод я ранее создал, потому что я уже знаю, что это работает. Я как раз проверял что функция работает в этом смысле.
Второй тест — testExceptionWhenFileIsTooLarge() — гарантирует, что результатом этого прохождения теста является то, что исключение выбрасывается. Обратите внимание, что я не использовал попробовать / поймать шаблон. Я использовал phpunit’s expectException. Это правильный способ сделать это в соответствии с руководством phpunit и делает его проще писать, чем работает утверждения внутри попробовать / поймать, но это означает, что тестовый код выглядит меньше, как кто-то будет на самом деле использовать код, который пахнет немного плохо для меня.
Третий тест — testAllowsFileSize() — убедитесь, что, когда правильный размер файла пройден, он работает, как ожидалось. Этот тест не делает ничего сверхконкретного. Я в основном добавил его, потому что существующие тесты не учитывают эти параметры. Это интеграционный тест, который потерпит неудачу, если одна из многих вещей пойдет не так. Какие тесты он не с будет указывать более четко, что проблема.
Это стоит того
Принятие тестового подхода к разработке может очень помочь, особенно по мере роста вашей команды. Даже для сольного разработчика, будучи вынужден думать о том, что изменения, которые необходимо сделать, прежде чем эти изменения имеет тонну выгоды. Кроме того, проведение тестов до внедрения означает, что вы не тратите время или мозг на тестирование или разработку способов тестирования.
Подумайте обо всех различных неофициальных тестах, которые вы создали во время работы над функцией. Сколько раз вы вызывали функцию и var’dump() результат, пока вы не получите правильный результат, а затем удалить этот код и двигаться дальше? Это тот же базовый подход, что и написание теста, который утверждает результат функции, что вы ожидаете. Разве вы не хотите, чтобы вы могли сохранить эти неофициальные тесты с вашей базой кода навсегда?
Источник: torquemag.io