替换测试中的特定模块
Replace specific module in testing
我正在使用 Jest 测试我的 React-Redux 应用程序,作为我的 API 调用的一部分,我正在导入一个获取模块 cross-fetch
。我想用 fetch-mock
覆盖或替换它。这是我的文件结构:
Action.js
import fetch from 'cross-fetch';
export const apiCall = () => {
return fetch('http://url');
Action.test.js
import fetchMock from 'fetch-mock';
import { apiCall } from './Action';
fetchMock.get('*', { hello: 'world' });
describe('actions', () => {
apiCall().then(
response => {
console.log(response)
})
})
显然此时我还没有设置测试。因为 cross-fetch 的导入更接近它使用它的 fetch 实现的函数,导致它执行实际调用而不是我的模拟。获取被模拟的最佳方法是什么(除了删除 import fetch from 'cross-fetch'
行)?
有没有办法根据调用的节点脚本是 test
还是 build
进行条件导入?或者将模拟提取设置为优先?
fetch-mock
并非旨在替换您正在测试的代码中的 fetch()
调用,您也不需要更改或删除任何导入。相反,它会在您的测试期间提供模拟响应,以便使用 fetch()
发出的请求收到已知的可靠响应。
如果你的项目是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);
});
});
注意:确保您没有在文件顶部导入被测模块。 require
调用完成了那部分。
有几种方法可以解决这个问题
使用名为 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 所做的,但我更喜欢 nock,因为文档要好得多)。
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);
})
});
为什么不在你的 Action.js 文件中导出一个 maker 函数,它会注入 fetch 方法,然后 returns 实际的 apiCaller:
Action.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
:
Action.test.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
函数签名引入另一个参数(如另一个答案中所建议的那样),从而保持向后兼容。
我正在使用 Jest 测试我的 React-Redux 应用程序,作为我的 API 调用的一部分,我正在导入一个获取模块 cross-fetch
。我想用 fetch-mock
覆盖或替换它。这是我的文件结构:
Action.js
import fetch from 'cross-fetch';
export const apiCall = () => {
return fetch('http://url');
Action.test.js
import fetchMock from 'fetch-mock';
import { apiCall } from './Action';
fetchMock.get('*', { hello: 'world' });
describe('actions', () => {
apiCall().then(
response => {
console.log(response)
})
})
显然此时我还没有设置测试。因为 cross-fetch 的导入更接近它使用它的 fetch 实现的函数,导致它执行实际调用而不是我的模拟。获取被模拟的最佳方法是什么(除了删除 import fetch from 'cross-fetch'
行)?
有没有办法根据调用的节点脚本是 test
还是 build
进行条件导入?或者将模拟提取设置为优先?
fetch-mock
并非旨在替换您正在测试的代码中的 fetch()
调用,您也不需要更改或删除任何导入。相反,它会在您的测试期间提供模拟响应,以便使用 fetch()
发出的请求收到已知的可靠响应。
如果你的项目是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);
});
});
注意:确保您没有在文件顶部导入被测模块。 require
调用完成了那部分。
有几种方法可以解决这个问题
使用名为 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 所做的,但我更喜欢 nock,因为文档要好得多)。
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); }) });
为什么不在你的 Action.js 文件中导出一个 maker 函数,它会注入 fetch 方法,然后 returns 实际的 apiCaller:
Action.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
:
Action.test.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
函数签名引入另一个参数(如另一个答案中所建议的那样),从而保持向后兼容。