Вопрос или проблема
В настоящее время я изучаю модульное тестирование в JavaScript, особенно сосредоточив внимание на заглушках, из книги “Искусство модульного тестирования”. Автор говорит о том, как сделать код более тестируемым, устранив зависимости, но я запутался, как применить эту концепцию, когда использую заглушки с внешними зависимостями, такими как moment().
Вот моя ситуация: у меня есть функция проверки пароля, которая выбрасывает ошибку, если она вызывается в выходные. Также есть обертка, verifyPasswordForCurrentDay
, которая использует moment().day()
для получения текущего дня недели:
const SUNDAY = 0, SATURDAY = 6;
const verifyPassword = (input, rules, currentDay) => {
if ([SATURDAY, SUNDAY].includes(currentDay)) {
throw new Error("Сегодня выходной!");
}
// здесь больше кода...
return [];
};
const verifyPasswordForCurrentDay = (input, rules) => {
const currentDay = moment().day();
return verifyPassword(input, rules, currentDay);
};
Мне удалось протестировать функцию verifyPassword, используя заглушку и передавая день недели в качестве параметра. Вот тест:
describe("verifier", () => {
test("в выходные дни выбрасывает исключения", () => {
expect(() => verifyPassword("любое введенное значение", [], SUNDAY)).toThrow(
"Сегодня выходной!"
);
});
});
Однако у меня возникли проблемы с тестированием verifyPasswordForCurrentDay
, так как она имеет зависимость от moment().day(). В “Искусстве модульного тестирования” автор предлагает устранять зависимости, чтобы сделать код тестируемым.
Меня также беспокоит, что мы лишь перенесли проблему на один уровень выше. verifyPasswordForCurrentDay
все еще имеет внешнюю зависимость (moment), что затрудняет тестирование. Используя инъекцию параметров, мы создаем тестируемую функцию (verifyPassword), но лишь переносим проблему на уровень выше, что делает обертку (verifyPasswordForCurrentDay
) нетестируемой или более сложной для тестирования. Это верно?
Ответ или решение
Для успешного тестирования функций, зависящих от внешних библиотек, таких как Moment.js, важно применять подходы, которые делают ваши функции более независимыми и тестируемыми. В вашем случае, вы правильно наметили проблему: функция verifyPasswordForCurrentDay
зависит от Moment.js, и это создает трудности при тестировании. Давайте разберем, как эффективно справляться с этой зависимостью.
Подход к решению проблемы: параметризация и инъекция зависимостей
-
Инъекция зависимостей: Вместо того чтобы напрямую вызывать
moment()
, вы можете передавать текущий день как параметр вашей функции. Это позволяет вам абстрагироваться от Moment.js и делает функции более универсальными и тестируемыми.Пример изменения функции:
const verifyPasswordForCurrentDay = (input, rules, getCurrentDay) => { const currentDay = getCurrentDay(); return verifyPassword(input, rules, currentDay); };
Здесь
getCurrentDay
— это функция, которую вы можете заменить при тестировании. В обычной эксплуатации вы можете использовать стандартную функцию() => moment().day()
. -
Создание заглушки для получения текущего дня: При тестировании
verifyPasswordForCurrentDay
вы можете создать заглушку (stub) для функцииgetCurrentDay
. Это позволит вам контролировать, какой именно день будет возвращаться вашей функции во время тестирования.Пример тестирования:
describe("verifyPasswordForCurrentDay", () => { test("on weekends, throws exceptions", () => { const getCurrentDayStub = () => SUNDAY; // Заглушка для получения текущего дня expect(() => verifyPasswordForCurrentDay("any input", [], getCurrentDayStub)).toThrow("It's the weekend!"); }); test("on weekdays, does not throw exceptions", () => { const getCurrentDayStub = () => 1; // Например, понедельник expect(() => verifyPasswordForCurrentDay("any input", [], getCurrentDayStub)).not.toThrow(); }); });
Преимущества этого подхода
- Гибкость: Вы можете тестировать функции с различными значениями текущего дня без необходимости реальных изменений в коде или зависимостях.
- Изоляция тестов: Ваши тесты на функциональность стали более независимыми и могут быть выполнены быстро без доступа к внешним библиотекам.
- Повторное использование кода: Вместо того, чтобы дублировать логику получения текущего дня, вы вынесли её в независимую функцию.
Заключение
Да, вы правы, что в вашем первоначальном примере проблемы были лишь перенесены на уровень выше. Однако, используя инъекцию зависимостей и стубы, вы сможете контролировать поведение ваших функций даже в присутствии внешних зависимостей. Этот подход сделает вашу архитектуру более чистой и ясно организованной, облегчая тестирование и поддержку кода в будущем.
Надеюсь, это поможет вам успешно справиться с тестированием ваших функций!