0
votes

I'm trying to test api routes that interact with a mongodb database through mongoose. My testing tools are chai and mocha. Each time, the test fails because of a timeout error.

Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.

The route is correctly called, and the function findUserById is also called. However, I note that findUserById is called before the server has connected to mongodb!

How to fix this?

Here is the app file:

require("dotenv").config();
import express from "express";
import bodyParser from "body-parser";
import compression from "compression";
import cookieParser from "cookie-parser";
import rateLimit from "express-rate-limit";
import cors from "cors";
import { createServer } from "http";
import { Server } from "socket.io";
import searchRouter from "./routers/search";
import { SocketControler } from "./controllers/socket";

// Server
export const app = express();
const server = createServer(app);
const io = new Server(server);

// Middleware
app.use(cors({ credentials: true, origin: [process.env.CLIENT as string] }));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(compression());
app.use(cookieParser());
app.use(rateLimit({ max: 100})); // limit each IP to 20 requests per minute
SocketControler.manageEvents(io);

// Routers
app.use(searchRouter);
app.locals.io = io;

// Start
connectToDatabase()
  .then(() =>
    server.listen(process.env.PORT, () =>
      console.log(`server is listening on ${process.env.PORT}!`)
    )
  )
  .catch((err) => console.log("error", err));

The route is:

interface GetUser extends Request {
  params: {
    id: string;
  };
}

interface IResponse extends Response {
  user: UserLean;
}

const getUser = async (
  req: GetUser,
  res: IResponse
): Promise<IResponse> => {
  try {
    const user = await UserControler.findUserById(req.params.id);
    return res.status(200).json({ user });
  } catch (err) {
    throw new Error("error.unknown");
  }
};

export default getUser;

The controler function:

  static findUserById(id: string) {
    return UserDB.findById({ _id: id }).lean();
  }

The connectToDatabase function:

import Mongoose from "mongoose";

export default async function connectToDatabase() {
  try {
    return Mongoose.connect(process.env.MONGODB as string, {
      useFindAndModify: false,
      useNewUrlParser: true,
      useUnifiedTopology: true,
      useCreateIndex: true,
      autoIndex: false,
    });
  } catch (err) {
    throw new Error(err);
  }
}

And the test file:

import chai from "chai";
import chaiHttp from "chai-http";
import { app } from "../index";

chai.use(chaiHttp);
const api = chai.request(app).keepOpen();

describe("GET /user/:id", () => {
  it("return user information", (done) => {
    api
      .get("/user/123")
      .end((err, res) => {
        chai.expect(res).to.have.status(200);
        done();
      });
  });
});

How can I fix this?

2

2 Answers

1
votes

This is how we do it:

First, we created the db connection as you have in connectToDatabase(), then we create a before hook for our tests:

before(async () => {
   await connectToDatabase();
   //... any other setup code that needs to occur
});

This ensures that the db connection occurs before the tests are run.

0
votes

The issue was that process.env.MONGODB couldn't be read. I needed to add require("dotenv").config(); at the top of the connectToDatabase.ts file.