Я новичок в Jest и пытаюсь написать несколько тестов для некоторого существующего кода. Хотя я наткнулся на множество статей Jest и сообщений Stackoverflow о фиктивных функциях в модуле (например, это), я считаю, что пытаюсь реализовать что-то, что либо немного продвинуто, либо очень глупо и требует рефакторинга кода. В любом случае, я не уверен и был бы очень признателен за помощь!
Позвольте мне объяснить мою ситуацию, используя пример кода. Предположим, мы имеем дело с системой электронной коммерции, и в бэкенде есть модуль для получения сведений о заказе из какого-то API:
// get-order-data.js
const getOrderDataFromApi = async (orderId) => {
// . . . some API call
console.info("The actual getOrderData function called!");
}
// Added only to emphasize that the entire module shouldn't be mocked
const getOrderDataFromDatabase = async (orderId) => { }
module.exports = {
getOrderDataFromApi,
getOrderDataFromDatabase,
}
И еще есть очень похожий модуль для получения данных о доставке заказа:
// get-delivery-data.js
const getDeliveryDataFromApi = async (orderId) => {
// . . . some API call
console.info("The actual getDeliveryDataFromApi function called!");
}
// Added only to emphasize that the entire module shouldn't be mocked
const getDeliveryDataFromDatabase = async (orderId) => { }
module.exports = {
getDeliveryDataFromApi,
getDeliveryDataFromDatabase,
}
Как вы можете видеть, в каждом модуле есть console.info
, который используется для обозначения того, что исходный код был выполнен вместо фиктивного кода.
Теперь предположим, что оба этих модуля используются другой функцией модуля, например:
// combine-order-data.js
const { getDeliveryDataFromApi } = require("./get-delivery-data");
const { getOrderDataFromApi } = require("./get-order-data")
const combineOrderData = async (orderId) => {
const orderData = await getOrderDataFromApi(orderId);
const deliveryData = await getDeliveryDataFromApi(orderId);
return {
orderData,
deliveryData,
};
}
module.exports = {
combineOrderData,
};
И именно поведение этой функции combineOrderData()
я хочу протестировать, смоделировав getOrderDataFromApi()
и getDeliveryDataFromApi()
.
Для этого я создал следующий тестовый файл:
// order.test.js
const { combineOrderData } = require("./combine-order-data");
const mockedOrderData = {
id: 1,
amount: 1000,
};
const mockedDelieryData = {
orderId: 1,
status: 'DELIVERED',
};
beforeEach(() => {
jest.mock("./get-order-data", () => {
getOrderDataFromApi: jest.fn(() => Promise.resolve(mockedOrderData))
});
jest.mock("./get-delivery-data", () => {
getDeliveryDataFromApi: jest.fn(() => Promise.resolve(mockedDelieryData))
});
});
test("combineOrderData correctly combines data", async () => {
const combinedOrder = await combineOrderData(111);
});
Когда я сейчас запускаю npx jest
, я вижу console.info
s в выводе, говорящие мне, что насмешка не удалась:
console.info
The actual getOrderData function called!
at getOrderDataFromApi (get-order-data.js:3:13)
console.info
The actual getDeliveryDataFromApi function called!
at getDeliveryDataFromApi (get-delivery-data.js:3:13)
PASS ./order.test.js
✓ combineOrderData correctly combines data (28 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.227 s
Ran all test suites related to changed files.
Я думаю, это происходит потому, что когда вызывается combineOrderData()
, он создает собственную копию используемых модулей (я думал, что модули Node по умолчанию являются синглтонами; например, существует только одна копия импортированного модуля, которая используется всеми require
)?
В любом случае, я не знаю, как правильно этого добиться.
Кроме того, не стесняйтесь комментировать, если структура кода нарушена и рефакторинг решит проблему, но я не вижу, как я могу избежать написания и тестирования кода, который вызывает другие вызовы функций, и поэтому, похоже, я столкнусь с этой проблемой. регулярно.
Я нашел 2 проблемы с тестом
jest.mock("./get-delivery-data", () => ({
getDeliveryDataFromApi: jest.fn(() => Promise.resolve(mockedDelieryData)),
}));
Сначала require
импортирует настоящие модули вместо фиктивных.
Рабочий тест
jest.mock("./get-order-data", () => ({
getOrderDataFromApi: jest.fn(() => Promise.resolve(mockedOrderData)),
}));
jest.mock("./get-delivery-data", () => ({
getDeliveryDataFromApi: jest.fn(() => Promise.resolve(mockedDelieryData)),
}));
const { combineOrderData } = require("./combine-order-data");
const mockedOrderData = {
id: 1,
amount: 1000,
};
const mockedDelieryData = {
orderId: 1,
status: "DELIVERED",
};
test("combineOrderData correctly combines data", async () => {
const combinedOrder = await combineOrderData(111);
});
Спасибо за тяжелую работу! Да, заказ импорта был большой ошибкой с моей стороны. Другое дело синтаксис. Функция, переданная
jest.mock
, по-видимому, должна возвращать объект, поэтому синтаксис жирной стрелки требует() => ({})
, а не() => {}
. Спасибо еще раз! :-)