Экстремальное программирование. Разработка через тестирование | страница 141
int fib(int n) {
if (n == 0) return 0;
return 1;
}
Дублирование в тестирующем методе начинает действовать мне на нервы. По мере добавления новых тестов, дублирование будет только усугубляться. Давайте попробуем выделить общую структуру операторов assert(), для этого добавим в тест таблицу входных и ожидаемых значений функции fib():
public void testFibonacci() {
int cases[][] = {{0,0},{1,1}};
for (int i = 0; i < cases.length; i++)
assertEquals(cases[i][1], fib(cases[i][0]));
}
Теперь добавление нового теста требует всего шесть нажатий на клавиши и никаких дополнительных строк:
public void testFibonacci() {
int cases[][] = {{0,0},{1,1},{2,1}};
for (int i = 0; i < cases.length; i++)
assertEquals(cases[i][1], fib(cases[i][0]));
}
Как это ни удивительно, но новый тест работает. Это происходит потому, что константа 1 также подходит и для входного значения 2. Переходим к следующему тесту:
public void testFibonacci() {
int cases[][] = {{0,0},{1,1},{2,1},{3,2}};
for (int i = 0; i < cases.length; i++)
assertEquals(cases[i][1], fib(cases[i][0]));
}
Ура! Наконец-то тест не сработал. Воспользуемся прежней стратегией (рассматриваем меньшие входные значения как специальные случаи):
int fib(int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
return 2;
}
Теперь мы готовы обобщить код. Мы написали 2, однако на самом деле мы имели в виду 1 + 1.
int fib(int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
return 1 + 1;
}
Первая единица в сумме – на самом деле fib(n-1):
int fib(int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
return fib(n-1) + 1;
}
Вторая единица в сумме – на самом деле fib(n-2):
int fib(int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
return fib(n-1) + fib(n-2);
}
Теперь окончательно чистим код. Та же самая структура должна работать для fib(2), поэтому мы можем преобразовать второй условный оператор:
int fib(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
return fib(n-1) + fib(n-2);
}
Это и есть функция вычисления последовательности Фибоначчи, целиком и полностью разработанная в рамках методики TDD.
Послесловие
Мартин Фаулер (Martin Fowler)
Когда рассказываешь о разработке, основанной на тестировании, сложнее всего передать то психическое состояние, в котором находишься, работая в стиле TDD. Я помню, как в ходе проекта C3 мы с Ральфом Битти (Ralph Beattie) работали над реализацией сложного набора условий выплаты. Ральф сформулировал набор соответствующих тестов, после чего мы приступили к реализации этих тестов одного за другим. Процесс был равномерным и неторопливым, из-за этого казалось, что мы работаем медленно. Однако, взглянув назад на проделанную работу, можно было понять, что, несмотря на кажущуюся неторопливость, мы работали очень даже быстро.