Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot call Express Next function after throwing Error (TypeScript) #788

Closed
ShinJustinHolly3317 opened this issue Sep 22, 2022 · 1 comment

Comments

@ShinJustinHolly3317
Copy link

ShinJustinHolly3317 commented Sep 22, 2022

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

  1. server.ts
// ...
// set up routes
app.use('/api/v1', routes);

// set up error handler
app.use(errorHandler);
// ....
  1. card data route
// ....
const router = express.Router();

router.post('/', asyncHandler(postCardDataHandler));

export default router;
  1. 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?

@ShinJustinHolly3317
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant