Безопасные шаги рефакторинга — Примеры
Пример 1 — Один коммит, который рефакторит и меняет поведение
Разработчик приводит в порядок функцию расчёта цены и заодно меняет округление. Тесты, которые он прогнал после этого, стали красными. Определите, чем на самом деле было изменение, и разделите его.
// до
function calc(v) { return Math.floor(v.reduce((s, i) => s + i.price, 0)); }
// после — переименовано, выделено и теперь округляет вверх
function totalPrice(items) {
return Math.round(sumPrices(items));
}
Решение
// шаг 1: только структура — тесты не менялись и зелёные
function calcTotal(items) { return Math.floor(sumPrices(items)); }
// шаг 2: только поведение — со своим новым тестом
test("округляет до ближайшего пенни", () => {
expect(calcTotal([{ price: 1.005 }])).toBe(1.01);
});
Прогоните тесты после шага 1: если они провалятся, виновато переименование или выделение. Затем прогоните их после шага 2: провал теперь может быть только из-за округления. Одна шляпа за раз — вот что делает прогон тестов информативным; смешав две задачи, вы оставляете открытыми обе гипотезы, и проверяющий не может сказать, какая половина диффа должна была сохранять поведение.
Пример 2 — Перестройка кода, у которого нет тестов
Вас попросили разбить одну большую функцию устаревшего форматтера на три, а тестов у неё нет. Покажите, как заслужить право двигать код прежде, чем его двигать.
function formatLegacy(input) {
// 80 строк: обрезает, дополняет, приводит к верхнему регистру, добавляет суффикс
}
// тестов нет; никто не знает, какие части намеренные
Решение
// три характеризующих теста, записанных с текущего кода
test("дополняет короткое значение", () => {
expect(formatLegacy("ab")).toBe("ab ");
});
test("обрабатывает пустое значение", () => {
expect(formatLegacy("")).toBe(" ");
});
test("приводит к верхнему регистру до дополнения", () => {
expect(formatLegacy("a b")).toBe("A B ");
});
// затем, когда сетка на месте: выделите одну ответственность, прогоните тесты
Эти тесты не утверждают, что форматтер верен, — один из них фиксирует дополнение значения, которое дополнять никому не нужно. Они утверждают лишь, что бы он ни делал сегодня, завтра он будет вести себя так же. Это и есть право, необходимое рефакторингу, и потому они написаны против текущей реализации до того, как двинулся хоть один кусок кода. Выделение без сетки — не рефакторинг, а догадка с избыточной уверенностью.