существуетРезюме разработки через тестирование (TDD) — принципыВ этой статье был сделан концептуальный обзор TDD. Лично я считаю недостатком теоретического знания то, что оно делает упор только на внешние стимулы и не учитывает внутренний психологический процесс обучающихся.Например, трудно установить картографическую связь с теоретическим знанием на основе имеющегося опыта.Поэтому объективная практика является единственный критерий проверки истины. Помня об этой цели, я потратил некоторое время на выбор кейсов в эти дни, потому что хорошая история или хороший кейс могут не только заставить меня чувствовать себя более вовлеченным, но и раскрыть очарование TDD.
предисловие
Статья включает кейсы, декомпозицию задач, дизайн интерфейса отчетной части, правила именования юнит-тестов и кейсовую практику TDD Исходный код статьи размещен вМой личный гитхаб, я надеюсь, что следующая серия статей о практике TDD также будет полезна для читателей.
Объем
TDD (разработка, управляемая тестированием) может иметь разное понимание в разных кругах и разных ролях.Во избежание двусмысленности в статье TDD упоминается как UTDD (Unit Test Driven Development), то есть «Разработка, управляемая модульным тестированием».
Подготовить
- Понимание ООП.
- Узнайте о Java 8.
- Ознакомьтесь с Intellij IDEA.
- Знакомые с теоретическими знаниями TDD, вы можете обратиться кРезюме разработки через тестирование (TDD) — принципы.
- Узнайте о Guice, упрощенной платформе внедрения зависимостей от Google.
- Знаком с использованием инструментов тестирования Junit и Mockito.
- Знаком с созданием автоматизированной среды модульного тестирования.
кейс
Вы учитель физкультуры и решаете начать игру за пять минут до окончания урока. На данный момент в классе 100 учеников. Правила игры таковы:
- Сначала вы называете три различных специальных числа, которые должны быть однозначными, например, 3, 5 и 7.
- Пусть все ученики выстраиваются в очередь и считают по порядку.
- Когда учащиеся сообщают числа, если сообщаемое число кратно первому специальному числу (3), то число не может быть произнесено, а может быть названо «Шип»; если сообщаемое число кратно второму специальному числу (5), то «Произнеси жужжание». ; если указанное число кратно третьему специальному числу (7), произнесите Whizz.
- Когда учащиеся сообщают числа, если сообщаемое число кратно двум специальным числам одновременно, требуется особая обработка, например, первое специальное число и кратное второму специальному числу, тогда число нельзя назвать, но FizzBuzz , и так далее. Если оно одновременно кратно трем специальным числам, скажите FizzBuzzWhizz.
- Когда учащиеся сообщают числа, если сообщаемое число содержит первое специальное число, они не могут назвать число, но должны сказать соответствующее слово.Например, в этом примере первое специальное число равно 3, тогда учащиеся, которые хотят сообщить 13, должны скажи Физз. Если номер содержит первый специальный номер, игнорируются правила 3 и 4. Например, учащиеся, которые хотят сообщить 35, сообщают только о Fizz, а не о BuzzWhizz.
Теперь нам нужно, чтобы вы завершили программу для моделирования этой игры, которая сначала принимает 3 специальных числа, а затем выводит числа или слова, которые должны сосчитать 100 учеников. Например:
войти:
3,5,7
Выход (фрагмент):
1,2,шипение,4,жужжание,шипение,жужжание,8,шипение,жужжание,11,шипение,шипение,жужжание,шипение,16,17,шипение,19,жужжание,…,100
Декомпозиция задачи
Анализ требований перед TDD может прояснить, какова цель задачи в начале, чтобы уменьшить низкоуровневые ошибки, вызванные непониманием; затем требования декомпозируются, и цель состоит в том, чтобы получить список задач, которые можно проверить . В процессе практики TDD вы также можете корректировать список задач, например, добавлять новые задачи или удалять лишние задачи и т. д.
В процессе анализа требований я сначала проанализирую роли, задействованные во всем кейсе, с точки зрения участников, и обнаружу, что в игре задействованы две роли, а именно учителя и ученики; а затем проанализирую с точки зрения участников. Обязанности Делается вывод, что обязанность учителя – инициировать игру, определить правила игры и назвать 3 уникальных однозначных числа, обязанность ученика – принять участие в игре и сообщить число согласно правилам игры. В итоге я изначально придумал следующий список задач:
- Начать игру.
- Определите правила игры.
- Произнесите 3 уникальных однозначных числа.
- Студенческий отчет.
- Проверьте входные параметры.
разбивка задачи
Я обнаружил, что на задачу «Отчет учащегося» влияет ряд правил игры, поэтому я уточню задачу и поищу особые потребности задачи, чтобы упростить ее планирование. В процессе анализа я буду отмечать более специальные или важные правила, чтобы не забыть. Вот моя разбивка списка дел:
- Начать игру.
- Определите правила игры.
- Произнесите 3 уникальных однозначных числа.
-
!!!Студенческий отчет.
- Если оно кратно первому специальному числу, сообщите Fizz.
- Если оно кратно второму специальному числу, сообщите Buzz.
- Сообщите о Whiz, если оно кратно третьему специальному числу.
- Если это одновременно несколько специальных номеров, вам необходимо соединить соответствующие слова в порядке специальных номеров, а затем сообщить о них, например, FizzBuzz, BuzzWhizz, FizzBuzzWhizz.
- Сообщайте о Fizz только в том случае, если он содержит первый специальный номер(Правила 1, 2, 3, 4 игнорировать).
- Если он не кратен специальному числу и не содержит первого специального числа, сообщается соответствующий порядковый номер.
- Проверьте входные параметры.
планирование миссии
Иногда декомпозированные задачи не отражают приоритеты и зависимости, поэтому для повышения эффективности работы необходимо спланировать задачи, чтобы сделать их более организованными и не потеряться в куче задач.
Из-за средней сложности дела места для этого шага не так много, однако на этом шаге можно подумать о том, с какой задачи следует начать, а критерии выбора могут бытьСсылаться наЭти три:
- важность задачи
- зависимости задач
- сложность задачи
О важности задачи судят, оценивая, является ли задача основным процессом.Например, важная программа «проверки входа» относительно невелика по сравнению с «количеством отчетов учащихся» и может быть выполнена позже.
Определите порядок задач, проанализировав зависимости задач. Конкретный приоритет варьируется от человека к человеку. Некоторым людям нравится использовать сверху вниз, а некоторым — снизу вверх. К счастью, Mock может помочь разработчикам изолировать зависимости, а также может управлять классами и интерфейсами через Mock, не полагаясь на конкретные реализации, избегая проблем с поиском последовательности задач.
Сложность задачи анализа должна быть получена путем анализа спроса и опыта.Проанализировав приведенные выше случаи, мы можем узнать, что сложность заключается в алгоритме отчетности студентов. Лично я, если это не основная миссия, я не выбираю сложную неосновную миссию в качестве стартовой в начале.
Завершить задачу
Благодаря анализу сложная задача «отчеты учащихся» является основной функцией всей игры, и, наконец, я решил сначала выполнить эту задачу.
Соглашение об именах тестов
- Тестовые классы называются XXXTest.
- Именование метода тестирования должно быть
should_xxx_when_xxx
,Например:should_return_false_when_1_is_greater_than_2
. - Логика кода тестового метода следует шаблону «Дано-когда-тогда».
Знание: Дано-Когда-Тогда
При написании тестовых методов следует следовать шаблону «Дано-когда-тогда» (при задании xx, при выполнении xx вы получите обратную связь xx). Этот шаблон позволяет разработчикам сосредоточиться и подумать о следующих вещах:
- Дано: это заставляет нас задуматься о том, в каком контексте находится этот тест и какие объекты используются, чтобы мы могли подумать о том, какие контексты и объекты необходимо создать.
- Когда: это заставляет нас задуматься с точки зрения пользователя, что это за поведение и какие входные данные оно имеет, чтобы подумать об именовании и параметрах метода.
- Затем: какая обратная связь управляет нашим мыслительным поведением, чтобы думать о возвращаемом значении метода.
Мысль: каково значение использования should_xxx_when_xxx в методе тестирования?
Благодаря идеям и инструментам BDD, этот метод именования был выработан мной в процессе практики BDD (конечно, я не только использую это соглашение о именовании), он включает, но не ограничивается следующими преимуществами:
- Вы можете сосредоточиться на поведении и не увязнуть в деталях реализации.
- Именование близко к естественному языку, намерение выражения ясно, читабельность высокая, а группа получателей широка.
- Хороший контроль над объемом тестов, начиная от поведения пользователя (с уклоном в сторону BDD) и заканчивая логическими ветвлениями (с уклоном в сторону TDD).
Теперь, когда требования уточнены, соглашение об именовании тестов составлено, а задача доработана, можно приступать к TDD.
Распространенные ошибки
Ранние привычки разработки (кодирование-выполнение-наблюдение) заставят разработчиков преждевременно вдаваться в детали реализации.Одним из недостатков этой привычки разработки является то, что цикл обратной связи длинный, что не способствует ритму маленьких шагов и быстрой работе. Поэтому в процессе практики TDD нужно постоянно напоминать себе о лозунгах и правилах TDD, а также культивировать в себе новые привычки мышления.
Разработка через тестирование
Завершить задачу
- Студенческий отчет.
- Если оно кратно первому специальному числу, сообщите Fizz.
- Если оно кратно второму специальному числу, сообщите Buzz.
- Сообщите о Whiz, если оно кратно третьему специальному числу.
- Если это одновременно несколько специальных номеров, вам необходимо соединить соответствующие слова в порядке специальных номеров, а затем сообщить о них, например, FizzBuzz, BuzzWhizz, FizzBuzzWhizz.
- Сообщайте о Fizz только в том случае, если он содержит первый специальный номер(Правила 1, 2, 3, 4 игнорировать).
- Если оно не кратно специальному числу и не содержит первого специального числа, сообщите Fizz.
Согласно общему процессу TDD, на этом этапе мне нужно подумать о том, что я хочу сделать, подумать о том, как это протестировать, а затем написать небольшой тест. Подумайте о необходимых классах, интерфейсах, входах и выходах.
Согласно предыдущему анализу спроса, учащиеся должны знать свои соответствующие серийные номера и правила игры, чтобы сообщить номер, поэтомуStudent
класс иString countOff(position, gameRules)
метод, наблюдениеcountOff
Метод обнаружил, что правила игры нужно использовать, поэтому он также вытесняетGameRule
Добрый.
Напишите достаточно кода, чтобы провалить тест (окончательная неудача лучше, чем неопределенное чувство).
@Test
public void should_return_fizz_when_just_a_multiple_of_the_first_number() {
List<GameRule> gameRules = new ArrayList<>();
assertThat(Student.countOff(3, gameRules)).isEqualTo("Fizz");
}
Когда этот код запускается, он не компилируется из-за отсутствия необходимых классов и методов, поэтому я быстро добавил следующий код:
public class Student {
public static String countOff(Integer position, List<GameRule> gameRules) {
return "";
}
}
public class GameRule {
}
Затем запустил модульные тесты и получил следующее сообщение об ошибке:
Напишите код, достаточный для того, чтобы тест прошел (гарантируйте, что тест, написанный ранее, также должен пройти).
После проверки на ошибки я обнаружил, что добавлениеGameRule
Это повлияло на мое суждение о кажущейся реализации кода, поэтому в этот момент я использовал стратегию псевдореализации, чтобы сделать тест пройденным как можно быстрее, чтобы кажущаяся реализация могла быть зафиксирована в непрерывной тонкой обратной связи, поэтому я быстро набрал следующее псевдокод:
public static String countOff(Integer position, List<GameRule> gameRules) {
return "Fizz";
}
К счастью, тест прошел, и я очень быстро получил желаемые результаты:
Я знаю, что с этим кодом что-то не так, теперь я думаю, стоит ли продолжать писатьGameRule
Сделать псевдореализацию очевидной? Или выберите следующую задачу и поставьте "НаписатьGameRule
"Будете ли вы это делать после того, как запишете это в список задач и подождете? Критерий этого отбора очень прост, то есть судить, сколько времени потребуется на выполнение задачи. Если это можно сделать быстро, то продолжайте сделать это. Если это займет некоторое время, то запишите это и перейдите к следующему заданию. Благодаря моему анализу, необходимо только датьGameRule
Добавление двух переменных-членов (числа и соответствующего термина) делает то, что я хочу,
Затем я скорректировал соответствующий тестовый код:
@Test
public void should_return_fizz_when_just_a_multiple_of_the_first_umbe() {
List<GameRule> gameRules = Lists.list(
new GameRule(3, "Fizz"),
new GameRule(5, "Buzz"),
new GameRule(7, "Whizz")
);
assertThat(Student.countOff(3, gameRules)).isEqualTo("Fizz");
}
Затем был добавлен следующий код:
public class GameRule {
private Integer number;
private String term;
public GameRule(Integer number, String term) {
this.number = number;
this.term = term;
}
...
}
public class Student {
public static String countOff(Integer position, List<GameRule> gameRules) {
if (position % gameRules.get(0).getNumber() == 0) {
return gameRules.get(0).getTerm();
}
return position.toString();
}
}
Затем запустите тест:
Отлично, получил обратную связь, что тест прошел быстро.Поскольку объем тестирования и кода в настоящее время очень мал, а явного дурного запаха нет, необходимости в рефакторинге пока нет, а текущая подзадача прямо перечеркивается и выбирается следующая подзадача. Из-за ограниченного поля статьи, после многократного повторения всего процесса TDD:
- Студенческий отчет.
Если оно кратно первому специальному числу, сообщите Fizz.Если оно кратно второму специальному числу, сообщите Buzz.- Сообщить о Whiz, если оно кратно третьему специальному числу(текущая задача).
- Если это одновременно несколько специальных номеров, вам необходимо соединить соответствующие слова в порядке специальных номеров, а затем сообщить о них, например, FizzBuzz, BuzzWhizz, FizzBuzzWhizz.
- Сообщайте о Fizz только в том случае, если он содержит первый специальный номер(Правила 1, 2, 3, 4 игнорировать).
Если он не кратен специальному числу и не содержит первого специального числа, сообщается соответствующий порядковый номер.
public class StudentTest {
private final List<GameRule> gameRules = Lists.list(
new GameRule(3, "Fizz"),
new GameRule(5, "Buzz"),
new GameRule(7, "Whizz")
);
@Test
public void should_return_1_when_mismatch_any_number() {
assertThat(Student.countOff(1, gameRules)).isEqualTo("1");
}
@Test
public void should_return_fizz_when_just_a_multiple_of_the_first_number() {
assertThat(Student.countOff(3, gameRules)).isEqualTo("Fizz");
assertThat(Student.countOff(6, gameRules)).isEqualTo("Fizz");
}
@Test
public void should_return_buzz_when_just_a_multiple_of_the_second_number() {
assertThat(Student.countOff(5, gameRules)).isEqualTo("Buzz");
assertThat(Student.countOff(10, gameRules)).isEqualTo("Buzz");
}
@Test
public void should_return_whizz_when_just_a_multiple_of_the_third_number() {
assertThat(Student.countOff(7, gameRules)).isEqualTo("Whizz");
assertThat(Student.countOff(14, gameRules)).isEqualTo("Whizz");
}
}
public class Student {
public static String countOff(Integer position, List<GameRule> gameRules) {
if (position % gameRules.get(0).getNumber() == 0) {
return gameRules.get(0).getTerm();
} else if (position % gameRules.get(1).getNumber() == 0) {
return gameRules.get(1).getTerm();
} else if (position % gameRules.get(2).getNumber() == 0) {
return gameRules.get(2).getTerm();
}
return position.toString();
}
}
В это время постепенно проявляется «дурной запах» кода, и необходимо ввести фазу рефакторинга, чтобы исключить повторение дизайна и сделать ритм маленьких шагов более стабильным.
Читайте цикл статей:
исходный код
GitHub.com/Ли Инин сказал/Слишком много…
Добро пожаловать в мою учетную запись подписки WeChat, я буду продолжать публиковать больше технических статей, я надеюсь, что мы сможем учиться друг у друга.