Статьи

Перестройте ваш код с помощью тестовой разработки

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

Разработка через тестирование (TDD) — это новая концепция, которая инструктирует разработчиков писать блок-тесты перед написанием даже одной строки кода.

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

Закон 1:  Вам не разрешается писать какой-либо производственный код, если только он не прошел неудачный тестовый блок.

Закон 2:  Вам не разрешено писать больше модульных тестов, чем достаточно для неудачи, а ошибки компиляции — это ошибки.

Закон 3:  Вам не разрешено писать больше производственного кода, чем достаточно для прохождения одного неудачного модульного теста.

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

Но вопрос в том, действительно ли модульные тесты так важны? Какие преимущества они действительно приносят в наш код? Является ли следование TDD единственным способом написания модульных тестов?

Ну, ответ на первый вопрос довольно прост: юнит-тесты действительно, очень важны,

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

Но TDD — не единственный способ достижения этих преимуществ; написание ваших модульных тестов после написания кода может в значительной степени обеспечить вам такую ​​же уверенность.

Однако TDD дает вам нечто гораздо более выгодное, чем просто уверенность в коде; это дает вам возможность лучше проектировать ваше приложение, и удивительно то, что вам не нужно ничего делать для этого. Следуя трем законам TDD, ваше окончательное приложение будет элегантно разработано и тщательно протестировано.

Давайте посмотрим на пример, чтобы понять, как это работает.

Предположим, я планирую написать код для бинарного поиска (для тех, кто не знаком с бинарным поиском, нажмите здесь ).

binarySearch( arr[],  left,  right,  x) 
{ 
    if (right >= left) { 
       mid = (left + right) / 2; 
        if (arr[mid] == x) 
            return mid; 
        if (arr[mid] > x) 
            return binarySearch(arr, left, mid - 1, x); 
        return binarySearch(arr, mid + 1, right, x); 
    } 
    return -1; 
} 

Бинарный поиск — это простой алгоритм поиска, имеющий  O (n)   временную сложность.

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

Основной принцип бинарного поиска состоит в том, чтобы разделить пространство поиска на две половины до тех пор, пока пространство для поиска не станет пустым или элемент не будет найден, и утверждение mid = (left + right) / 2играет важную роль в определении направления поиска.

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

Вызывает беспокойство утверждение: mid = (left + right) / 2

В чем проблема с этим утверждением? Если leftи rightполучится достаточно большим, их сумма будет переполнена и станет отрицательной, а деление на 2 снова останется отрицательным, поэтому это утверждение вернет отрицательное число как среднее из двух положительных чисел, что математически невозможно. Эта строка довольно неудачно провалилась бы, пытаясь найти среднее из 2 чисел.

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

Ответ TDD.

TDD на помощь

Если бы мы следовали законам разработки через тестирование, к их слову, мы были бы на этапе, когда перед написанием mid = (left + right) / 2 ,нам нужно было бы написать тест для вычисления среднего значения, но как написать тест для полуфабрикатной функции? ? Ответ — нет; Вы извлекаете это логическое утверждение в отдельную функцию и затем пишете тест для него.

Давайте назовем это  calculateMean. Теперь наш код будет выглядеть примерно так:

calculateMean(first, second){
  return (first+second)/2;
}   

binarySearch( arr[],  left,  right,  x) 
{ 
    if (right >= left) { 
       mid = calculateMean(left,right); 
        if (arr[mid] == x) 
            return mid; 
        if (arr[mid] > x) 
            return binarySearch(arr, left, mid - 1, x); 
        return binarySearch(arr, mid + 1, right, x); 
    } 
    return -1; 
} 

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

После этого наш код будет выглядеть так:

calculateMean(first, second){
return first + (second - first)/2;
} 

binarySearch( arr[], left, right, x) 
{ 
if (right >= left) { 
mid = calculateMean(first,second); 
if (arr[mid] == x) 
return mid; 
if (arr[mid] > x) 
return binarySearch(arr, left, mid - 1, x); 
return binarySearch(arr, mid + 1, right, x); 
} 
return -1; 
} 

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

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

Следуйте за мной в твиттере на  @IamShivamMohan