【问题标题】:jest.setTimeout.Error: Mocking Express middleware with Jest and Supertestjest.setTimeout.Error:使用 Jest 和 Supertest 模拟 Express 中间件
【发布时间】:2020-10-19 00:23:39
【问题描述】:

我想模拟 auth 中间件函数以始终只调用 next()。为了尝试实现这一点,我在测试文件的开头添加了以下内容,然后将 auth 中间件功能添加到 app.js 中的应用程序中。

jest.mock('../../middleware/auth.js', () =>
  // ensure func is mocked before being attached to the app instance
  jest.fn((req, res, next) => next()) // seems to only work for first request that hits this middleware
); // Mock authentication

然后我在 auth 中间件中添加了一些调试,但在任何测试中都没有命中它们。

目前我正在使用以下方法,当beforeEach() 函数没有被注释掉时,测试全部通过:

role.test.js

jest.mock('../../middleware/auth.js', () =>
  // ensure func is mocked before being attached to the app instance
  jest.fn((req, res, next) => next()) // seems to only work for first request that hits this middleware
); // Mock authentication

const request = require('supertest');
const app = require('../../app.js');
const Role = require('../../../db/models/index.js').Role;
// const auth = require('../../middleware/auth.js');
/**
 * Test role API routes
 */
describe('role-router', () => {
  let res;
  const token = 'valid-token';
  const baseUrl = '/private/api/roles';

  // Without the `beforeEach()` only the first request sent using supertest will use the mocked functionality
  // With the `beforeEach()` everything functions as expected, but why?
  // beforeEach(() => {
  //   auth.mockImplementation((req, res, next) => next());
  // });

  describe(`Create new role | post ${baseUrl}`, () => {
    describe('When successful', () => {
      beforeAll(async () => {
        // this will use the proper mocked functionality
        res = await request(app)
          .post(baseUrl)
          .set('Authorization', token)
          .send({
            roleName: 'Secret Agent',
            roleDesc: "World's best secret agent",
            ...
          });
      });

      // passes
      it('Should return 201', () => {
        expect(res.status).toBe(201);
      });
    }); // When successful
  }); // Create new role

  describe(`Delete role by id | delete ${baseUrl}/:id`, () => {
    describe('When successful', () => {
      beforeAll(async () => {
        const role = await Role.create({
          roleName: 'Secret Agent',
          roleDesc: "World's best secret agent",
          ...
        });
        
        // fails does not get response, res remains the same as res from previous test
        res = await request(app)
          .delete(`${baseUrl}/${role.id}`)
          .set('Authorization', token)
          .send();
      });

      // fails with 201
      it('Should return 204', () => {
        expect(res.status).toBe(204);
      });
    }); // When successful
  }); // Delete role
}); 

收到错误:

 ● role-router › Delete role by id | delete /private/api/roles/:id › When successful › Should return 204

    Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.

      at mapper (node_modules/jest-jasmine2/build/queueRunner.js:29:45)

app.js

// Dependencies
const express = require('express');
const auth = require('./middleware/auth.js');
...

/**
 * Express application
 */
const app = express();

// Middleware
app.use(express.json());
...

// Private routes are secured with jwt authentication middleware
app.all('/private/*', (req, res, next) => auth(req, res, next));

// Public routes
...

// Private routes
app.use('/private/api/roles/', require('./components/role/role-router.js'));
...

module.exports = app;

任何想法为什么在每次测试之前不使用 beforeEach() 函数来模拟中间件功能时这不起作用?也许我错过了更严重的事情?

【问题讨论】:

  • 我看到的与auth 相关的唯一可能性是您启用了resetMocks。在任何其他情况下,jest.mock 中的jest.fn(() => ...) 不应与auth.mockImplementation 不同。如果这是真的,请考虑将 resetMocks 替换为 restoreMocks。
  • 感谢@EstusFlask,resetMocks 配置参数确实是真的。将其设置为 false 即可解决问题。
  • 很高兴就这么简单。如果有人遇到类似问题,请发布答案。

标签: node.js express jestjs supertest


【解决方案1】:

出现这种行为的原因是存在spy.mockReset()jest.resetAllMocks() 或启用了resetMocks 配置选项。使用restoreMocks 选项代替合理的默认配置。

jest.fn(() => ...)jest.fn().mockImplementation(() => ...) 之间的区别在于它们对jest.restoreAllMocks() 的响应方式。 restoreAllMocks 恢复原始实现,在jest.fn() 的情况下是无操作的,而() => ...jest.fn(...) 的情况下被认为是原始实现。所以通常jest.fn(() => ...) 实现不会被重置。

jest.resetAllMocksjest.restoreAllMocks 之间的区别在于前者可以使jest.fn(...) 成为noop,但不会将间谍恢复到原始实现。这特别影响在模拟模块中提供的间谍,每个测试套件使用jest.mock,但仍然不会恢复全局间谍。由于这种行为很少需要,所以jest.restoreAllMocks()restoreMocks 配置选项通常更可取。如果需要重置 jest.fn(...) 实现,可以专门为此间谍完成 mockResetmockImplementation

【讨论】:

    猜你喜欢
    • 2021-01-13
    • 2021-08-15
    • 2014-05-14
    • 2019-10-07
    • 2013-10-26
    • 1970-01-01
    • 2021-11-28
    • 2019-07-31
    • 2020-01-17
    相关资源
    最近更新 更多