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

Что такое ТДД?

TDD расшифровывается как Test-Driven Development и означает, что вы уделяете внимание тестированию, а не реальному производственному коду.

Три закона TDD регулируют процесс, которому вы должны следовать при использовании TDD. Вот они:

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

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

Как начать работать с TDD

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

  • учитывая дату въезда и выезда на парковку, вернуть правильную цену.
  • каждый час стоит 1 доллар
  • мы всегда будем использовать отработанные часы для расчета стоимости, поэтому, если автомобиль стоит на стоянке 1:10 часа, мы будем брать 2 часа

Хорошо, теперь, когда мы знаем правила, мы можем приступить к разработке нашего кода. Поскольку мы используем TDD, первым шагом является создание теста.

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

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

Поскольку мы используем машинописный текст, мы можем использовать объект Date для передачи этой информации.

Итак, для первого теста у нас будет что-то вроде этого:

test("should return the correct price", () => {
  const enterDate = new Date('2022-03-27T10:00:00')
  const leaveDate = new Date('2022-03-27T12:00:00')
  
  const price = calculateParkingTicket(enterDate, leaveDate)
  expect(price).toBe(2)
})

Теперь, когда у нас есть неудачный тест, мы можем, наконец, приступить к работе над настоящей функцией.

Итак, следуя TDD, мы создаем объем кода, необходимый для прохождения теста:

export const calculateParkingTicket = (enter: Date, leave: Date) => {
  return 2
}

Теперь у нас проходной тест!! У нас есть достаточно кода, чтобы ваш тест прошел, хотя этот код не решает полностью нашу настоящую проблему.

Если реальная проблема еще не решена, мы напишем новый тест!

test("should return the correct price when 5 hours passed", () => {
  const enterDate = new Date('2022-03-27T10:00:00')
  const leaveDate = new Date('2022-03-27T15:00:00')
  const price = calculateParkingTicket(enterDate, leaveDate)
  expect(price).toBe(5)
})

Хорошо, теперь мы не можем возвращать только число 2 из нашей функции. Нам нужно разработать более сложное решение.

const PRICE_PER_HOUR = 1
export const calculateParkingTicket = (enter: Date, leave: Date) => {
  const diferencesInMs = (leave.getTime() - enter.getTime())
  const diferencesInHours = diferencesInMs / (1000 * 60 * 60)
  return diferencesInHours * PRICE_PER_HOUR
}

Выглядит лучше! Но что произойдет, если машина останется припаркованной на 2:30 часа? по нашим правилам он должен стоить 3 доллара. Итак, давайте напишем тест для этого случая

test("should return the correct price 2:30 hours passed", () => {
  const enterDate = new Date('2022-03-27T10:00:00')
  const leaveDate = new Date('2022-03-27T12:30:00')
  
  const price = calculateParkingTicket(enterDate, leaveDate)
  expect(price).toBe(3)
})

Теперь, когда у нас есть новый тест, мы можем начать программировать!

const PRICE_PER_HOUR = 1
export const calculateParkingTicket = (enter: Date, leave: Date) => {
  const diferencesInMs = (leave.getTime() - enter.getTime())
  const diferencesInHours = diferencesInMs / (1000 * 60 * 60)
  const hoursToPay = Math.ceil(diferencesInHours)
  return hoursToPay * PRICE_PER_HOUR
}

Итак, на этом наш код заканчивается. Каждое правило учитывается и проверяется.

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

Заключение

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

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

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

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

Первоначально опубликовано на https://www.nfaustino.com.