Cannot call Express Next function after throwing Error (TypeScript)
ShinJustinHolly3317 opened this issue · comments
Env:
typescript 4.7.4
Node 14
Supertest 6.2.4
Jest 28.1.3
Express 4.18.1
Express-async-Handler 1.2.0
Test Code
import server, { app } from '/server.ts'
const mockGetCache = jest.fn();
const mockSetCache = jest.fn();
// This is mocking class
jest.mock('../../src/infra/clients/memcached-client', () => jest.fn().mockImplementation(() => ({
getCache: mockGetCache,
setCache: mockSetCache,
})));
it('POST cardData Error', async () => {
mockGetCache.mockImplementation(() => {
throw new Error('test-error in POST cardData');
});
const response = await request(app)
.post('/api/v1/ad/cardData')
.send(fakeCardData);
expect(response.statusCode).toBe(500);
expect(response.body).toEqual({ code: 9999, message: 'test-error in POST cardData' });
});
Application code
- server.ts
// ...
// set up routes
app.use('/api/v1', routes);
// set up error handler
app.use(errorHandler);
// ....
- card data route
// ....
const router = express.Router();
router.post('/', asyncHandler(postCardDataHandler));
export default router;
- postCardDataHandler.ts
try {
const { cardId } = req.body;
const { appId } = req.body;
// init repository
const cacheClient = new MemcachedClient();
const memcachedCardRepository = new MemcachedCardRepository(cacheClient);
const postCardUsecase = new PostCardUsecase(memcachedCardRepository);
// execute usecase
const keywords = await postCardUsecase.exec(adId, appId);
return res.status(200).json(keywordsDto);
} catch (err) {
console.error('[some warning]', err)
return next(err)
}
Simple workflow
My code doesn't respond 500 to client directly. I use express error handler to catch all error and responds error to client here.
So I also use express-async-handler to catch unexpected error, which will call next()
and let error-handler catch the error.
Problem:
When I start server, everything works fine with both postman, curl, or my Frontend Website and Mobile.
However it went into Timeout problem when I do integration test with supertest
.
Below is log
● ad server unit test ›POST cardData Error
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."
Possible reason
next
function not working normally.
After digging deep to the express-async-handler
, and I found that everything is good with it.
At the final step express-async-handler
also call next(err)
, but my error-handler didn't catch the err
.
found similar issue here #529
update
I tried remove express router, and use the handler directly by app.post(...)
in server.ts
like
// ...
// set up routes
app.use('/api/v1',(req, res, next) => {
try {
//...
} catch (err) {
console.log('[handler Error]', err)
return next(err)
}
});
// set up error handler
app.use(errorHandler);
// ....
This will work correctly.
Still have no clue why.
anyone can save me?
This issue is because of jest.useFakeTimer
will probably mock out some event loop function under next()
.
Prevent using jest.useFakeTimer
will fix this issue.