在 express 应用程序中测试默认错误处理程序会导致超时

Testing default error handler in an express application results in a timeout

测试快速应用程序的默认错误处理程序时,会导致超时。该函数如下所示:

const createApp = (underlyingFunction) => {
  const app = express()
  app.get('/my-endpoint', async (req, res) => {
    await underlyingFunction()
    res.send({ success: true })
  })

  const errorHandler: ErrorRequestHandler = (error, req, res, next) => {
    console.error('Unhandled exception');
    console.error(error);
    console.error(error.stack);
    res.status(500).send({
      message: 'Oh dear',
    });
    // next()
  }

  app.use(errorHandler)
  return app;
}
  

测试如下所示:

  test('error should be handled and return 500', async () => {
    underlyingFunction.mockImplementation(() => {
      throw new Error('Something went wrong')
    })

    const app = createApp(underlyingFunction)
    const response = await request(app).get('/my-endpoint')

    expect(response.status).toBe(500)
  })

当运行测试时,出现如下错误:

    thrown: "Exceeded timeout of 5000 ms for a test.
    Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."

可能是什么原因造成的?

对于 express V4,从 Error Handling#Catching Errors 文档中,我们知道:

For errors returned from asynchronous functions invoked by route handlers and middleware, you must pass them to the next() function, where Express will catch and process them.

虽然测试用例中mock的underlyingFunction是同步的,但是在路由中,async/await语法将这个路由handler转为异步代码

因此,您需要使用 try...catch 语句来捕获 underlyingFunction 函数引发的错误。并将 error 传递给 next 函数。 express 将使用 error.

将请求路由到错误处理程序中间件

例如

app.ts:

import express from 'express';
import { ErrorRequestHandler } from 'express-serve-static-core';

export const createApp = (underlyingFunction) => {
  const app = express();

  app.get('/my-endpoint', async (req, res, next) => {
    try {
      await underlyingFunction();
      res.send({ success: true });
    } catch (error) {
      next(error);
    }
  });

  const errorHandler: ErrorRequestHandler = (error, req, res, next) => {
    console.error('Unhandled exception');
    res.status(500).send({ message: 'Oh dear' });
  };

  app.use(errorHandler);
  return app;
};

app.test.ts:

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

describe('68923821', () => {
  test('error should be handled and return 500', async () => {
    const underlyingFunction = jest.fn().mockImplementation(() => {
      throw new Error('Something went wrong');
    });
    const app = createApp(underlyingFunction);
    const res = await request(app).get('/my-endpoint');
    expect(res.status).toEqual(500);
  });
});

测试结果:

 PASS  examples/68923821/app.test.ts (9.128 s)
  68923821
    ✓ error should be handled and return 500 (49 ms)

  console.error
    Unhandled exception

      15 | 
      16 |   const errorHandler: ErrorRequestHandler = (error, req, res, next) => {
    > 17 |     console.error('Unhandled exception');
         |             ^
      18 |     res.status(500).send({ message: 'Oh dear' });
      19 |   };
      20 | 

      at errorHandler (examples/68923821/app.ts:17:13)

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