Unit Test with Express / Mocha / Mongodb Memory Server

Viewed 116

I would like to configure my project in order to run unit test for some API endpoints (that call the database). I'm using :

  • ExpressJS
  • MongoDB (no Mongoose)
  • Mocha / Chai
  • Mongodb Memory Server (to mock the DB)
// app.ts

export const app = express();

const port = process.env.PORT;

app.use("/my-route", myRoutes);

mongoConnect().then(() => {
  app.listen(port, () => {
    console.log(`Listening on port ${port}`);
  });
});
// database.ts

export const mongoConnect = async () => {
  try {
    let MONGODB_URI = process.env.MONGODB_URI;

    if (process.env.NODE_ENV === "test") {
      const mongoServer = await MongoMemoryServer.create();
      MONGODB_URI = mongoServer.getUri();
    }

    const client: MongoClient = await MongoClient.connect(MONGODB_URI);
    _db = client.db("dbName");
    _mongoClient = client;

    if (process.env.NODE_ENV === "test") {
      console.log("Connected to MongoDB Test");
    } else {
      console.log("Connected to MongoDB");
    }
  } catch (err) {
    console.log("Error connecting to MongoDB:", err);
    throw err;
  }
};

export const getMongoClient = () => {
  if (_mongoClient) {
    return _mongoClient;
  }
  throw "Mongo client doesn't exist";
};

export const getDb = () => {
  if (_db) {
    return _db;
  }
  throw "No database found!";
};
// test.ts

  let mongoClient: MongoClient;
  let db: Db;

  before(function (done) {
    mongoConnect()
      .then(() => {
        db = getDb();
        mongoClient = getMongoClient();
        return db.createCollection("wordsCollection");
      })
      .then(() => {
        db.collection("wordsCollection").insertMany(data);
      })
      .catch((err) => console.log(err))
      .finally(() => done());
  });

  after(function (done) {
    db.dropDatabase();
    mongoClient.close().then(() => {
      done();
    });
  });

  it("test", async function () {
    let res = await chai
      .request(app)
      .post("/my-route/hello")
      .send({ excludeIds: [] });

    expect(res.status).to.equal(200);
  });
});

But it's not working...

If I call mongoConnect() in test.ts it console.log twice Connected to MongoDB Test. But if I don't call the function it throws me error because MongoClient is undefined.

I think await chai.request(app) already calls the database and server but I need to create Collection and Documents before. So I need to connect to the DB before the test.

Any help would be greatly appreciated.

1 Answers

I found a solution, I don't know if it's best practice but it works and is pretty easy, thanks to this post : https://stackoverflow.com/a/70285190/10547153.

I needed to add a condition in app.ts before making the connection to the database and the server in order to launch them only if it's called by Node itself.

if (require.main === module) {
  mongoConnect().then(() => {
    app.listen(port, () => {
      console.log(`Listening on port ${port}`);
    });
  });
}

When a file is run directly from Node.js, require.main is set to its module. That means that it is possible to determine whether a file has been run directly by testing require.main === module.

Now I can connect to the mocked database from test.ts and only one connection will be triggered.

Related