Записки Вредного программиста

enjoy, motherfuckers ;)

Введение в TDD с Ruby и Rspec

Здравствуйте, уважаемые разработчики, сегодня я коснусь немного новой для себя методологии, а именно TDD, что означает test driven development (разработка через тестирование).

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

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

Итак, поехали! Для начала нам понадобится gem rspec, который и будет нашим тестирующим инструментом, почему он, если есть аналоги? Потому что он первый попался мне на глаза, может есть и лучше, я пока не вникал в эту сторону вопроса, как известно – лучшее – враг хорошего.

gem install rspec

Команда установит нам необходимый гем со всеми необходимыми зависимостями.

Далее создаем папку проект (.). Дерево нашего проекта будет иметь вид:

1
2
3
4
5
.
├── _
├── calculator.rb
└── spec
    └── calculator_spec.rb

calculator.rb – наше приложение, spec/calculator_spec.rb – наш файл с тестами.

Создайте, если у вас еще не созданы, эти файлы, calculator.rb оставьте пустым. Начнем разработку, как уже говорилось вначале заметки с тестов, но перед этим давайте напишем небольшой план по разработке.

  • Должен существовать класс калькулятора
  • Калькулятор должен иметь метод add (сумма)
  • Метод add должен возвращать сумму двух своих аргументов
  • Должен иметь метод sub (разность)
  • Также и для методов mul (произведение) и div (частное)
  • Метод div должен кидать исключение, когда пытаемся делить на 0
  • (не относящийся к делу пункт) Класс Calculator должен быть проивзодным от Calculator

Напишем первый тест

1
2
3
4
5
6
require '../calculator'
describe 'Calculator' do
  it 'should exists' do
      Calculator.new
  end
end

Что же мы получаем, в первой строке все просто – подключаем файл нашего приложени, ведь над ним будем проводить наши эксперименты (без него никак). Со слова descibe начинается контекст теста, входящая в него конструкция (it ‘should exists’) непосредственно сам тест. Остается только запустить тест и посмотреть на результат:

rspec ./spec/calculator_spec.rb

Чтобы написать минимальный код, который бы прошел данный тест, нужно две строчки, а именно создать класс Calculator, так и запишем в ./calculator.rb

1
2
class Calulcator
end

Запускаем наш тест еще раз и убеждаемся, что тест пройден. Напишем новый тест, заключим его в тот же describe, что и предыдущий. У нас получится следующее:

1
2
3
4
5
6
7
8
describe 'Calculator' do
  it 'should exists' do
      Calculator.new
  end
  it 'should have method add(a,b)' do
      Calculator.new.add 2,3
  end
end

Тест говорит следующее: “Калькулятор должен иметь метод add, который принимает 2 параметра”, запускаем наш тест и видим, что !!! тест провален, потому что у нас метода add, тем более принимающего два параметра, так в чем же дело, давайте его напишем, ведь это всего две строки кода.

1
2
3
4
5
class Calculator
  def add(a, b)

  end
end

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

1
2
3
4
it 'add() method should add two numbers' do
  Calculator.new.add(2, 2).should_not eq(5)
  Calculator.new.add(2, 2).should eq(4)
end

И, собственно, код, данный тест проходящий:

1
2
3
def add(a, b)
  a+b
end

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
require '../calculator'

describe 'Calculator' do
  it 'should exists' do
      Calculator.new
  end
  it 'should have method add(a,b)' do
      Calculator.new.add 2,3
  end

  it 'add() method should add two numbers' do
      Calculator.new.add(2, 2).should_not eq(5)
      Calculator.new.add(2, 2).should eq(4)
  end

  it 'sub() method should return subtraction' do
      Calculator.new.sub(5, 3).should eq(2)
  end

  it 'mul() method should return multiple' do
      Calculator.new.mul(2, 7).should eq(14)
  end

  it 'div() method should return division' do
      Calculator.new.div(10, 2).should eq(5)
  end

  it 'div() method should raise exception by division by zero' do
      expect { Calculator.new.div(2, 0) }.to raise_error(ZeroDivisionError)
  end
end

Заключение

В данной заметке мы очень поверхностно познакомились с очень интересной методологией разработки TDD (test driven development) и разобрали небольшой пример, чтобы немного закрепить знания. Если вы будете применять данную методику в своих проектах, то количество ошибок в них будет асимптотически приближаться к нулю, это ли не счастье программиста? :)

Комментарии