如何使用 node-fetch、supertest 和 typescript 设置 fetch-mock
How to setup fetch-mock with node-fetch, supertest and typescript
我正在尝试向我的节点类型脚本项目添加一些笑话测试。我想使用 supertest 调用我的 koa 路由器,但也使用 fetch-mock 来模拟使用 node-fetch 发出的请求。
目前为止我的解决方案如下,但是我的路由器中的获取不使用带有 fetch-mock 的模拟获取设置。单元测试失败,因为没有返回我模拟的预期响应。我已经尝试按照 documentation 进行全局提取模拟,但没有成功,并且打字稿很难遵循我发现的非打字稿解决方案。我想尽可能避免使用非全局沙箱,因为我将不得不重新编写大量代码来传递获取。
server.spec.ts
import * as fetchMock from 'fetch-mock';
import * as request from 'supertest';
import server from './server';
afterEach(() => {
server.close();
fetchMock.reset();
});
describe('router', () => {
test('GET: should return data', async () => {
const expectedResponse = { test: 'TEST' };
fetchMock.get('https://myapi.com/test', expectedResponse);
const response = await request(server).get('/test');
expect(response.status).toEqual(200);
expect(response.body).toMatchObject(expectedResponse);
});
});
server.ts
import * as Koa from 'koa';
import * as Router from 'koa-router';
import fetch from 'node-fetch';
const app = new Koa();
const router = new Router();
router.get('/test', async ctx => {
const options = { method: 'GET' };
try {
const response = await fetch('https://myapi.com/test', options);
ctx.body = await response.json();
} catch (error) {
error.fetchUrl = url;
throw error;
}
});
app.use(router.routes());
const server = app.listen(3000);
export default server;
您可以自己手动模拟 node-fetch
模块。这是解决方案:
server.ts
:
import Koa from 'koa';
import Router from 'koa-router';
import fetch from 'node-fetch';
const app = new Koa();
const router = new Router();
router.get('/test', async ctx => {
const options = { method: 'GET' };
const url = 'https://myapi.com/test';
try {
const response = await fetch(url, options);
ctx.body = await response.json();
} catch (error) {
error.fetchUrl = url;
throw error;
}
});
app.use(router.routes());
function createHttpServer() {
return app.listen(3000);
}
if (require.main === module) {
createHttpServer();
}
export default createHttpServer;
server.spec.ts
:
import request from 'supertest';
import createHttpServer from './server';
import fetch from 'node-fetch';
const { Response } = jest.requireActual('node-fetch');
const server = createHttpServer();
jest.mock('node-fetch', () => jest.fn());
afterAll(done => {
server.close(done);
});
describe('router', () => {
test('GET: should return data', async () => {
const expectedResponse = { test: 'TEST' };
(fetch as jest.MockedFunction<typeof fetch>).mockResolvedValueOnce(new Response(JSON.stringify(expectedResponse)));
const response = await request(server).get('/test');
expect(response.status).toEqual(200);
expect(response.body).toEqual(expectedResponse);
});
test('GET: should throw error', async () => {
const mockedFetchError = new Error('some error');
(fetch as jest.MockedFunction<typeof fetch>).mockRejectedValueOnce(mockedFetchError);
const response = await request(server).get('/test');
expect(response.status).toEqual(500);
});
});
包含覆盖率报告的单元测试结果:
PASS src/Whosebug/56735795/server.spec.ts (8.487s)
router
✓ GET: should return data (51ms)
✓ GET: should throw error (15ms)
console.error node_modules/koa/lib/application.js:200
undefined
console.error node_modules/koa/lib/application.js:201
Error: some error
at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/Whosebug/56735795/server.spec.ts:26:30)
at step (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/Whosebug/56735795/server.spec.ts:32:23)
at Object.next (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/Whosebug/56735795/server.spec.ts:13:53)
at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/Whosebug/56735795/server.spec.ts:7:71
at new Promise (<anonymous>)
at Object.<anonymous>.__awaiter (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/Whosebug/56735795/server.spec.ts:3:12)
at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/Whosebug/56735795/server.spec.ts:25:35)
at Object.asyncJestTest (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:102:37)
at resolve (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
at new Promise (<anonymous>)
at mapper (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
at promise.then (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
at process._tickCallback (internal/process/next_tick.js:68:7)
console.error node_modules/koa/lib/application.js:202
undefined
-----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files | 95.24 | 50 | 100 | 94.12 | |
server.ts | 95.24 | 50 | 100 | 94.12 | 28 |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 10.36s
这是完整的演示:https://github.com/mrdulin/jest-codelab/tree/master/src/Whosebug/56735795
我尝试按照其他答案中的建议获得 node-fetch
mocks working with jest
and typescript using mocking and a bunch of packages。这是唯一对我有用的东西;
install 'jest-fetch-mock'
创建测试;
// To mock fetch we need to import and enable before all other imports
import { enableFetchMocks } from 'jest-fetch-mock';
enableFetchMocks();
import ...
import ...
import ...
import fetch from 'node-fetch';
describe("I lost too many hours on this", () => {
let spyFetch;
beforeAll(() => {
spyFetch = (fetch as jest.MockedFunction<typeof fetch>);
spyFetch.mockImplementation(() => Promise.resolve({json: () => 'YMMV'}));
});
afterEach(() => {
spyFetch.mockClear();
});
it("Call method which contains fetch", async () => {
// Setup
...
await exampleClass.methodContainingFetch('TestUrl');
expect(spyFetch.mock.calls[0][0]).toBe('TestUrl');
});
});
如果您在使用打字稿时遇到问题 type-hinting,有一段时间我使用
spyFetch = (fetch as any)
上面缺少超级测试,但我怀疑遇到这个问题的人在这部分有问题。
我正在尝试向我的节点类型脚本项目添加一些笑话测试。我想使用 supertest 调用我的 koa 路由器,但也使用 fetch-mock 来模拟使用 node-fetch 发出的请求。
目前为止我的解决方案如下,但是我的路由器中的获取不使用带有 fetch-mock 的模拟获取设置。单元测试失败,因为没有返回我模拟的预期响应。我已经尝试按照 documentation 进行全局提取模拟,但没有成功,并且打字稿很难遵循我发现的非打字稿解决方案。我想尽可能避免使用非全局沙箱,因为我将不得不重新编写大量代码来传递获取。
server.spec.ts
import * as fetchMock from 'fetch-mock';
import * as request from 'supertest';
import server from './server';
afterEach(() => {
server.close();
fetchMock.reset();
});
describe('router', () => {
test('GET: should return data', async () => {
const expectedResponse = { test: 'TEST' };
fetchMock.get('https://myapi.com/test', expectedResponse);
const response = await request(server).get('/test');
expect(response.status).toEqual(200);
expect(response.body).toMatchObject(expectedResponse);
});
});
server.ts
import * as Koa from 'koa';
import * as Router from 'koa-router';
import fetch from 'node-fetch';
const app = new Koa();
const router = new Router();
router.get('/test', async ctx => {
const options = { method: 'GET' };
try {
const response = await fetch('https://myapi.com/test', options);
ctx.body = await response.json();
} catch (error) {
error.fetchUrl = url;
throw error;
}
});
app.use(router.routes());
const server = app.listen(3000);
export default server;
您可以自己手动模拟 node-fetch
模块。这是解决方案:
server.ts
:
import Koa from 'koa';
import Router from 'koa-router';
import fetch from 'node-fetch';
const app = new Koa();
const router = new Router();
router.get('/test', async ctx => {
const options = { method: 'GET' };
const url = 'https://myapi.com/test';
try {
const response = await fetch(url, options);
ctx.body = await response.json();
} catch (error) {
error.fetchUrl = url;
throw error;
}
});
app.use(router.routes());
function createHttpServer() {
return app.listen(3000);
}
if (require.main === module) {
createHttpServer();
}
export default createHttpServer;
server.spec.ts
:
import request from 'supertest';
import createHttpServer from './server';
import fetch from 'node-fetch';
const { Response } = jest.requireActual('node-fetch');
const server = createHttpServer();
jest.mock('node-fetch', () => jest.fn());
afterAll(done => {
server.close(done);
});
describe('router', () => {
test('GET: should return data', async () => {
const expectedResponse = { test: 'TEST' };
(fetch as jest.MockedFunction<typeof fetch>).mockResolvedValueOnce(new Response(JSON.stringify(expectedResponse)));
const response = await request(server).get('/test');
expect(response.status).toEqual(200);
expect(response.body).toEqual(expectedResponse);
});
test('GET: should throw error', async () => {
const mockedFetchError = new Error('some error');
(fetch as jest.MockedFunction<typeof fetch>).mockRejectedValueOnce(mockedFetchError);
const response = await request(server).get('/test');
expect(response.status).toEqual(500);
});
});
包含覆盖率报告的单元测试结果:
PASS src/Whosebug/56735795/server.spec.ts (8.487s)
router
✓ GET: should return data (51ms)
✓ GET: should throw error (15ms)
console.error node_modules/koa/lib/application.js:200
undefined
console.error node_modules/koa/lib/application.js:201
Error: some error
at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/Whosebug/56735795/server.spec.ts:26:30)
at step (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/Whosebug/56735795/server.spec.ts:32:23)
at Object.next (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/Whosebug/56735795/server.spec.ts:13:53)
at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/Whosebug/56735795/server.spec.ts:7:71
at new Promise (<anonymous>)
at Object.<anonymous>.__awaiter (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/Whosebug/56735795/server.spec.ts:3:12)
at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/Whosebug/56735795/server.spec.ts:25:35)
at Object.asyncJestTest (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:102:37)
at resolve (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
at new Promise (<anonymous>)
at mapper (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
at promise.then (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
at process._tickCallback (internal/process/next_tick.js:68:7)
console.error node_modules/koa/lib/application.js:202
undefined
-----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files | 95.24 | 50 | 100 | 94.12 | |
server.ts | 95.24 | 50 | 100 | 94.12 | 28 |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 10.36s
这是完整的演示:https://github.com/mrdulin/jest-codelab/tree/master/src/Whosebug/56735795
我尝试按照其他答案中的建议获得 node-fetch
mocks working with jest
and typescript using
install 'jest-fetch-mock'
创建测试;
// To mock fetch we need to import and enable before all other imports
import { enableFetchMocks } from 'jest-fetch-mock';
enableFetchMocks();
import ...
import ...
import ...
import fetch from 'node-fetch';
describe("I lost too many hours on this", () => {
let spyFetch;
beforeAll(() => {
spyFetch = (fetch as jest.MockedFunction<typeof fetch>);
spyFetch.mockImplementation(() => Promise.resolve({json: () => 'YMMV'}));
});
afterEach(() => {
spyFetch.mockClear();
});
it("Call method which contains fetch", async () => {
// Setup
...
await exampleClass.methodContainingFetch('TestUrl');
expect(spyFetch.mock.calls[0][0]).toBe('TestUrl');
});
});
如果您在使用打字稿时遇到问题 type-hinting,有一段时间我使用
spyFetch = (fetch as any)
上面缺少超级测试,但我怀疑遇到这个问题的人在这部分有问题。