TDD

Пример разработки «Крестиков‑ноликов» по TDD

Введение 🔗

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

Основной посыл TDD — в разбиении больших задач на маленькие. Такой метод позволяет писать более структурированный, надёжный и читаемый код.

Стандартный цикл разработки состоит из трёх этапов и занимает 10–15 минут.

Схема цикла разработки по методологии TDD
Изображение взято с сайта codedream.me

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

Второй этап — зелёная зона. На нём реализуется функция, которая проходит этот тест. Цикл короткий, поэтому реализация должна быть максимально простой.

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

В этой статье мы напишем игру «Крестики‑нолики» на JS, используя практику TDD.

Разработку мы поделим на две части:

Это объёмная статья. Возможно, вам захочется сразу перейти к результатам.

Структура проекта и инструменты 🔗

Для разработки игры мы будем использовать:

  • Jest для написания и запуска тестов;
  • JSDOM для проверки функций, работающих с DOM;
  • Webpack для сборки проекта.
package.json
{ "scripts": { "test": "NODE_OPTIONS=--experimental-vm-modules npx jest", "build": "webpack" }, "devDependencies": { "jest": "^27.4.7", "jsdom": "^11.12.0", "webpack": "^5.67.0", "webpack-cli": "^4.9.2" } }

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

webpack.config.js
import webpack from 'webpack' import path from 'path' export default [{ name: 'client', mode: 'production', context: path.resolve(), entry: { javascript: ['./src/index.js'] }, output: { filename: './js/bundle.js', path: path.resolve('dist'), }, }]