如何将异步调用的结果传递给 Jest 中的共享函数

How to pass in the result of an async call to shared functions in Jest

问题

我正在测试 API 个端点并且有一些对每个端点都相同的测试。我不想复制和粘贴,而是想在可重用的功能中分享它。我还希望为每个函数将一条语句打印到控制台(通过 test),所以我不想组合逻辑。

代码

这里是应该分享的功能:

export function basicTests(res) {
  test("should respond with a 200 status code", () => {
    expect(res.statusCode).toBe(200);
  });

  test("should specify json in the content type header", () => {
    expect(res.headers["content-type"]).toEqual(expect.stringContaining("json"));
  });

  test("should return an ok property", () => {
    expect(res.body.ok).toBeDefined();
  });

  test("should return a description property", () => {
    expect(res.body.description).toBeDefined();
  });
}

下面是我在另一个文件中调用测试的方式:

let res = {}
beforeAll(async () => {
  const response = await request(app)
    .get("/some/api/check")
    .send();

  res = { ...response };
});

describe("GET /some/api/route", () => {
  basicTests(res);
  // ... more tests
});

什么不起作用

目前当我传入res时,它始终是一个空对象。因此,即使 beforeAll 在测试之前运行,变量似乎更早传递,因此在 Promise 解析时不会更新。

我该如何解决这个问题?

同步定义测试用例。这意味着当 basicTests 函数执行时 res 是空对象 {}

beforeAll(fn, timeout):

Runs a function before any of the tests in this file run. If the function returns a promise or is a generator, Jest waits for that promise to resolve before running tests.

这意味着在执行测试用例函数之前,beforeAll 中传递的函数将是 运行。由于函数 returns 是一个承诺,jest 等待那个承诺。

主要区别在于声明和运行时间。

一种解决方案是将 beforeAll 内容放入 basicTests 函数中。例如

basicTests.ts:

import request from 'supertest';

export function basicTests(app, route) {
  describe('basic test suites', () => {
    let res;
    beforeAll(async () => {
      const response = await request(app).get(route);
      res = { ...response };
    });
    test('should respond with a 200 status code', () => {
      expect(res.statusCode).toBe(200);
    });

    test('should specify json in the content type header', () => {
      expect(res.headers['content-type']).toEqual(expect.stringContaining('json'));
    });

    test('should return an ok property', () => {
      expect(res.body.ok).toBeDefined();
    });

    test('should return a description property', () => {
      expect(res.body.description).toBeDefined();
    });
  });
}

app.ts:

import express from 'express';

const app = express();

app.get('/some/api/check', (req, res) => {
  res.json({ ok: true, description: null, data: 'a' });
});

export { app };

app.test.ts:

import { basicTests } from './basicTests';
import request from 'supertest';
import { app } from './app';

describe('GET /some/api/route', () => {
  basicTests(app, '/some/api/check');
  let res;
  beforeAll(async () => {
    const response = await request(app).get('/some/api/check');
    res = { ...response };
  });
  test('should return data', () => {
    expect(res.body.data).toBe('a');
  });
});

测试结果:

 PASS  Whosebug/70702998/app.test.ts (8.972 s)
  GET /some/api/route
    ✓ should return data
    basic test suites
      ✓ should respond with a 200 status code (2 ms)
      ✓ should specify json in the content type header
      ✓ should return an ok property
      ✓ should return a description property

Test Suites: 1 passed, 1 total
Tests:       5 passed, 5 total
Snapshots:   0 total
Time:        9.042 s