如何使用节点获取,超级测试和打字稿

时间:2019-06-24 11:41:25

标签: node.js typescript unit-testing jestjs fetch-mock

我正在尝试向我的节点typecipt项目添加一些开玩笑的测试。我想使用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;

2 个答案:

答案 0 :(得分:4)

您可以自己手动模拟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/stackoverflow/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/stackoverflow/56735795/server.spec.ts:26:30)
          at step (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/56735795/server.spec.ts:32:23)
          at Object.next (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/56735795/server.spec.ts:13:53)
          at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/56735795/server.spec.ts:7:71
          at new Promise (<anonymous>)
          at Object.<anonymous>.__awaiter (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/56735795/server.spec.ts:3:12)
          at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/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/stackoverflow/56735795

答案 1 :(得分:1)

我尝试使用mocks typescript manual和一堆node-fetch globaljestmocking一起工作different packages,如其他答案中所建议。这是唯一对我有用的东西;

安装'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');
    });
});

如果您在打字稿类型提示方面遇到问题,我使用了一段时间

spyFetch = (fetch as any)

上面的内容缺少超级测验,但我怀疑在这个问题上绊脚石的人们是否对此部分有疑问。