Имитация перегруженных методов TS с помощью Jest сама по себе не сложна, но вы должны знать, как это сделать.
В этой статье я объясню «проблему» и как ее обойти.
Проблема"
Перегрузка методов — очень знакомая концепция для любого разработчика, привыкшего к объектно-ориентированному программированию (кстати, одна из многих, которые я объясню в своей будущей книге).
С помощью перегрузки методов вы можете определить несколько методов/функций с одинаковыми именами, но с разными сигнатурами. Перегрузку удобно использовать, поскольку конкретная реализация вызывается в зависимости от параметров, которые вы передаете.
Конечно, перегрузка методов — полезная концепция, а не проблема сама по себе. Однако во время тестирования может оказаться немного сложно имитировать перегруженные методы с помощью TypeScript.
Проблема в том, что когда мы просим TypeScript предоставить нам параметры перегруженного метода, мы получаем параметры последней перегрузки. Из-за этого у нас могут возникнуть проблемы, когда компилятор жалуется на то, что фиктивная реализация не принимает правильные параметры или не возвращает ожидаемый тип. Это одно из ограничений ReturnType
и условных типов.
Поначалу это может немного сбивать с толку.
Тогда, конечно, возникает проблема: как сообщить TypeScript, что мы хотим имитировать ту или иную версию функции?
Пример
Вот пример, показывающий проблему.
В моем текущем проекте я использую библиотеку nano для подключения к базе данных CouchDB. В частности, я использую nano в так называемых классах репозитория, которые отвечают за взаимодействие с базой данных.
При написании модульных тестов для новой операции «массового удаления» нам нужно было убедиться, что массовое удаление делает то, что нужно. Поскольку мы говорим о модульных тестах, мы действительно хотели сымитировать соединение с базой данных nano, а также вызов базы данных массовой операции.
С nano, когда мы устанавливаем соединение с базой данных, мы возвращаем объект типа DocumentScope<D>
. Среди многих других функций интерфейс включает функцию bulk
, которую можно использовать для выполнения массовых операций.
Эта функция на самом деле существует в двух вариантах:
Как видите, в зависимости от того, как он вызывается, мы возвращаем Promise<DocumentBulkResponse[]>
или Promise<DocumentInsertResponse[]>;
.
Второй вариант соответствует операции массовой вставки, а первый (тот, который нам нужно было смоделировать) — массовому удалению.
Обычно, когда мы имитируем функции с помощью Jest, мы делаем это следующим образом:
Это хорошо работает для случаев, когда есть один вариант, но в нашем случае нам нужно указать TypeScript, какой вариант мы имитируем; в противном случае мы получим ошибку компиляции, которая поначалу может ввести в заблуждение:
Теперь, когда вы знаете о двух вариантах функции, вы можете понять, откуда берется эта ошибка; TypeScript ожидает, что мы будем соблюдать сигнатуру (и, следовательно, также возвращаемый тип) второго варианта.
Мех!
Итак, как нам обойти эту проблему?
Решение
Решение этой проблемы с тестированием на самом деле довольно простое:
- Определите настраиваемый тип, соответствующий сигнатуре варианта, который вы хотите имитировать.
- Приведите тип, чтобы использовать тот, который вы только что определили перед созданием макета.
Вот пример:
С этим TypeScript больше не жалуется. Конечно, это всего лишь обходной путь, который иногда может вызывать проблемы (например, если изменяются сигнатуры фиктивных функций), но это полезный прием для продвижения вперед в тестировании.
Использованная литература:
- https://stackoverflow.com/questions/61752367/how-to-mock-overload-method-in-jest
- https://github.com/marchaos/jest-mock-extended/issues/20
- https://stackoverflow.com/questions/52760509/typescript-returntype-of-overloaded-function/52761156#52761156
- https://github.com/DefinitelyTyped/DefinitelyTyped/issues/34889
Вывод
В этой статье я объяснил, как бороться с перегрузками при имитации функций с помощью Jest и TypeScript.
Есть и другие способы справиться с этим, но этот подход довольно прост (хотя и «слабый»). Официальная рекомендация команды TS заключается в том, что более общие перегрузки должны определяться последними, но некоторые библиотеки не всегда соблюдают это (в основном потому, что это не так хорошо известно, я думаю).
Вот и все на сегодня!
PS: Если вы хотите узнать массу других интересных вещей о программном обеспечении/веб-разработке, вы можете предварительно заказать мою будущую книгу, получить копию моей книги по TypeScript и/или подписаться на мой информационный бюллетень!