Заменить конкретный модуль при тестировании
Я тестирую свое приложение React-Redux с шуткой, и как часть этого в моих вызовах API я импортирую модуль выборки cross-fetch
. Я хочу переопределить или заменить это на fetch-mock
. Вот моя файловая структура:
действие.js
import fetch from 'cross-fetch';
export const apiCall = () => {
return fetch('http://url');
действие.тест.js
import fetchMock from 'fetch-mock';
import { apiCall } from './Action';
fetchMock.get('*', { hello: 'world' });
describe('actions', () => {
apiCall().then(
response => {
console.log(response)
})
})
очевидно, на данный момент я не настроил тест. Поскольку перекрестная выборка импортируется ближе к функции, она использует ее реализацию fetch, заставляя ее Сделай сам звонок вместо моей шутки. Каков наилучший способ заставить выборку быть высмеянной (кроме удаления import fetch from 'cross-fetch'
line)?
есть ли способ выполнить условный импорт в зависимости от того, вызван ли скрипт узла test
или build
? Или установить издевательскую выборку в приоритет?
4 ответов
Если ваш проект является проектом webpack, то https://github.com/plasticine/inject-loader очень полезно. Вы можете просто поменять любую зависимость с макетом всего за несколько строк кода.
describe('MyModule', () => {
let myModule;
let dependencySpy;
beforeEach(() => {
dependencySpy= // {a mock/spy};
myModule = require('inject-loader!./MyModule')({
'cross-fetch': {dependencySpy},
});
});
it('should call fetch', () => {
myModule.function1();
expect(dependencySpy.calls.length).toBe(1);
});
});
Примечание: убедитесь, что вы не импортируете тестируемый модуль в верхней части файла. the require
вызов это.
fetch-mock
не предназначен для замены fetch()
вызовы кода, который вы тестируете, и вам не нужно изменять или удалять импорт. Вместо этого он предоставляет макет ответов во время тестов, чтобы запросы, сделанные с fetch()
получать известные, надежные ответы.
есть несколько способов, вы можете подойти к этой проблеме
Вы можете модули заглушки без инъекции зависимостей с помощью Sinon.
используйте lib под названием rewire издеваться над импортированными методами в процедурных звонки
-
повторно напишите исходную функцию, чтобы вы не использовали импорт напрямую
const apiCall = (url, {fetchFn = fetch}) => fetchFn(url); describe("apiCall", () => { it("should call fetch with url", () => { const fetchFn = sinon.spy(); const EXPECTED_URL = "URL"; apiCall(EXPECTED_URL, {fetchFn}); expect(fetchFn.firstCall.args).to.deep.equal([EXPECTED_URL]); }) });
-
перехватить запрос и утверждать на ответ (это, похоже, то, что fetch-mock делает, однако я предпочитаю НОК поскольку документация намного лучше).
describe("apiCall", () => { it("should call fetch with url", () => { const EXPECTED_URL = "URL"; const EXPECTED_RESPONSE = 'domain matched'; var scope = nock(EXPECTED_URL) .get('/resource') .reply(200, EXPECTED_RESPONSE); return expect(apiCall(EXPECTED_URL)).to.equal(EXPECTED_RESPONSE); }) });
Почему бы также не экспортировать функцию maker в свой действие.js файл, который получит метод выборки, а затем возвращает фактический apiCaller:
действие.js
// this export allows you to setup an apiCaller
// with a different fetcher than in the global scope
export const makeApiCaller = (fetch) => (url, opts) => fetch(url, opts);
// this export is your default apiCaller that uses cross-fetch
export const apiCall = makeApiCaller(fetch);
тогда в ваших тестах вы можете где-то создать экземпляр своего ApiCaller, например, в before
:
действие.тест.js
import fetchMock from 'fetch-mock';
import { makeapiCaller } from './Action';
fetchMock.get('*', { hello: 'world' });
// ...
let apiCall;
before(() {
apiCall = makeApiCaller(fetch); // injects mocked fetch
});
describe('actions', () => {
apiCall('/foo/bar').then(
response => {
console.log(response)
})
})
Примечание: преимущество этого способа заключается в том, что вам не нужно вводить другой аргумент ваш apiCall
подпись функции (как предложено в другом ответе) и, таким образом, оставаться обратно совместимыми.