Search code examples
node.jsexpressjestjssupertest

Jest and supertest: Test keep exceeding timeout


Hello I am a bit confused by this error I have encountered. I am working on an Universal React App using Webpack 5 and Express.

I want to implement Jest support by using the React-testing-Library for the frontend (which work) and supertest for the backend (this is where I am blocked). I am following this basic tutorial recommended by the jest doc himself in order to use jest on an node express environment. But everytime I get this 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."

Here are my code:

server.js

import app from './app.js';
import { mongooseConnection, disconnectMongoDB } from "./routers/services/url/urlDB.js"; // we call mongooseConnect var to connect only once into the mongoDB database
const PORT = process.env.PORT || 8080;


// the server listen on the port set by node on localhost.
app.listen(PORT, () => {
    console.log(
        `Server listening on \x1b[42m\x1b[1mhttp://localhost:${PORT}\x1b[0m in \x1b[41m${process.env.NODE_ENV}\x1b[0m`,
    );
});

// when when we shut down the app we execute a callback function before closing the server
process.on('exit', function() {
    disconnectMongoDB();
});

app.js

import express from 'express';
import path from 'path';
import cors from 'cors';
import {envIsProduction, envIsDevelopment} from './envmode/envUtil.js';
import { enableHMR } from './reload/hotReload.js';

let app = express();

// if we have set the environnent on production then:
if (envIsProduction()) {
    console.log(" _______________________________________ ");
    console.log("|                                       |");
    console.log("|             ( PRODUCTION )            |");
    console.log("|_______________________________________|");
    console.log(" ");

    app.use(express.static(path.join(__dirname,'../client'))); // we serve static file like the bundle-app.js to the browser from the current directory where the server is executed and we move to the top root to access the file
}
else if (envIsDevelopment()) {
    console.log(" _______________________________________ ");
    console.log("|                                       |");
    console.log("|             ( DEVELOPMENT )           |");
    console.log("|_______________________________________|");
    console.log(" ");

    enableHMR(app); // we enable the Hot MPodule Reload on the frontend and the backend
}

app.use(cors());
app.use(express.urlencoded({extended:false}));
app.use(express.json());

//Hot reload!
//ALL server routes are in this module!
app.use((req, res, next) => {
    require("./routers/routers")(req, res, next);
});

export default app;

routers.js

import renderPage from "./renderpage/renderPage.js";
import { serverRoutes, reactRouterRoutes, getReactRouterRoutesString } from "./routes.js";
import express from "express";
import routerLoginDB from "./request/routerLoginDB.js";
import routerSignupDB from "./request/routerSignupDB.js";
const router = express.Router();

// Put all your server routes in here

// When the user connect to the root of the server we send the page
router.get(serverRoutes.root, renderPage);

// When the user send a get request by the /click route a console.log and a respone is send.
router.get(serverRoutes.click, (req, res)=>{
    res.status(200).send("Click");
});

// when this user want to login into his account, we ask for the routerLoginDB to handle it
router.post(serverRoutes.login,routerLoginDB);

// when this user want to signup into his account, we ask for the routerSignupDB to handle it
router.post(serverRoutes.signup, routerSignupDB);

// For all the routes that only react-router need to use, if we refresh on a nested route of the react-router from the client side then we redirect it to the root route "/"
router.get(reactRouterRoutes,(req,res) => {
  res.redirect("/");
});

router.get("*", (req,res) =>{
    res.status(404).send('page not found');
}); //For all other type of request excluding the one specified here, we send back a 404 page;

module.exports = router;

app.test.js

import request from '../utils/test-node-utils.js'

describe("Test the /click path", () => {
  test("It should response the GET method", () => {
    return request
      .get("/click")
      .expect(200);
  });
});

and finally test-node-utils.js

import supertest from "supertest";
import app from "../serverside/app.js";

const request = supertest(app);

export default request;

Don't believe what the error say because I think it is more deep than that.

I have tried to increased the jest timeout value but it keep being stuck and reach the timeout limit. I have done exactly like the tutorial say without using my project structure and it worked but when I try to implement the tutorial in my backend structure, it don't work with supertest. I think it is related to my files or backend structure that make it don't work with the test.

Thanks in advance for your help


Solution

  • sorry for the late update, I solved this problem a long time ago and I forgot to add some more details in the initial post. To be short, my app was using various technologies like React-redux, mongoose and React-router. The tutorial was a bad idea because it was creating the same app with the same store and provider for every tests, so that might explain the huge latency.

    Instead I needed to create a custom render function for the tests so that I create a new Redux store instance every time it's called, with an optional preloadedState value that can be used for an initial value Alternately you could pass in an already-created Redux store instance and it automatically wrap the component being tested with a <Provider store={store}>

    Here is the function:

    import React from 'react'
    import { render } from '@testing-library/react'
    import { Provider } from 'react-redux'
    import { setupStore, persistor } from '../src/store/store.js';
    import {BrowserRouter} from "react-router-dom";
    import { PersistGate } from 'redux-persist/integration/react';
    
    // Automatically create a store instance if no store was passed in
    export function renderWithProviders(ui,{preloadedState = {},store = setupStore(preloadedState),...renderOptions} = {}) {
      function Wrapper({ children }) {
        return (
          <BrowserRouter>
            <Provider store={store}>
                <PersistGate loading={null} persistor={persistor}>
                  {children}
                </PersistGate>
              </Provider>
          </BrowserRouter>
        );
      }
    
      return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) }
    
    

    For the backend I didn't use supertest at the end but did just make a fetch request to the serverUrl like this:

    import fetch from 'cross-fetch';
    import { serverUrl } from "./routers/routes.js"
    
    
    // This test only test the click path
    describe("Test the /click path", () => {
      test("It should response the GET method", async () => {
        const result = await fetch(`${serverUrl()}/click`).then(res => res.text());
        expect(result).toBe('Click');  // Success!
      });
    });
    

    Thank you for all of your help.