Back to blog

Что такое TDD? Почему я выбираю Test-Driven Development!?

Я уже немного касался этого в предыдущем посте.


Что же такого в этой Test-Driven Development? В чем особенности и преимущества этой методологии?


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


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

Главные принципы Test-Driven Development:

  1. Сначала пишется тест
  2. Пишется ровно столько кода, сколько нужно для того, чтобы тест прошел
  3. Если необходимо, то устраняется дублирование и производится рефакторинг
  4. Пишется новый тест, описывающий дополнительные детали поведения программы
  5. Возвращаемся к пункту №2


И так до тех пор пока реализуемая часть программы не будет работать так как задумано. 


Я надеюсь, что небольшой пример внесет ясность. (Напишу его на JavaScript)

К примеру, нам нужно реализовать функцию, которая конвертирует граммы в килограммы.

Мы ожидаем, что мы передадим в эту функцию значение в граммах, а на выходе получим значение в килограммах. 


Первый тест, который мы напишем, может выглядеть так(Я пока нарочно не использую никакие Фреймворки для тестирования, потому что хочу показать принцип):


console.log(grammsToKg(1000) === 1);


Я просто вызвал функцию и жду, он вернет мне 1, потому что 1000 граммов - это 1 килограмм.


Если запустить этот код, то будет выброшено исключение


Uncaught ReferenceError: grammsToKg is not defined


, потому что такой функции нет. Чтобы исключения не было достаточно объявить эту функцию:


function grammsToKg(value

{

}

console.log(grammsToKg(1000) === 1);


Исключения больше нет, но console.log() выводит false, потому что наша функция ничего не возвращает. Следуя методологии TDD мы должны совершить минимум действий, чтобы починить тест. Рефакторингом и доработками займемся на следующих шагах.


Что нужно чтобы тест прошел?


function grammsToKg(value)

{

    return 1;

}


console.log(grammsToKg(1000) === 1);



Теперь console.log() выводит true. Тест прошел! Ура. Мы реализовали, то что хотели? Функция работает корректно для 1000 граммов. Давайте добавим еще один тест


function grammsToKg($value)

{

    return 1;

}


console.log(grammsToKg(1000) === 1);

console.log(grammsToKg(100) === 0.1);


Ведь 100 граммов - это 0,1 кг, верно? Но второй console.log выводит false, потому что функция всегда возвращает константу. 

Нам нужно исправить реализацию функции, чтобы починить тест: 


function grammsToKg(value)

{

    return value / 1000;

}


console.log(grammsToKg(1000) === 1);

console.log(grammsToKg(100) === 0.1);



В одном килограмме 1000 граммов, значит надо передаваемое в функцию значение делить 1000.


Теперь оба наших console.log выводят true. 


Для уверенность мы можем создать некий набор для проверки работы функции на большем количестве значений:


const values = [

    { g: 1000, kg: 1 },

    { g: 100, kg: 0.1 },

    { g: 5000, kg: 5 },

    { g: 6, kg: 0.006 }

];


Это массив объектов, в каждом из которых есть поле g, которое соответсвует значению в граммах, его мы будем передавать в качестве аргумента в нашу функцию, и есть поле kg - это то, что мы ждем в качестве возвращаемого значения от функции в этом случае.

И запустим в цикле проверку:


function grammsToKg(value)

{

    return value / 1000;

}


const testData = [

    { g: 1000, kg: 1 },

    { g: 100, kg: 0.1 },

    { g: 5000, kg: 5 },

    { g: 6, kg: 0.006 }

];


testData.map(item => {

    console.log(grammsToKg(item.g) === item.kg);

});


Если запустим, то мы увидим в консоле 4 раза true.

Вот мы реализовали свою первую функцию используя подход TDD.


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

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


Для начала нам нужно понять принципы #TDD.

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

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

Пишем новый тест для проверки работы программы с другими условиями.

Исправляем реализацию, делаем рефакторинг и т д пока программа не будет завершена.


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


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


В процессе мы постоянно запускаем код и имеет четкое представление как он работает.


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


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


Далее будет больше примеров. Сложнее и интереснее. 


Обсудить это можно в чате - https://teleg.run/progersclub

Почитать канал в телеграм - https://teleg.run/it_programmist

© 2019 shogenov.com