bulletproof-nodejs icon indicating copy to clipboard operation
bulletproof-nodejs copied to clipboard

Using a different database for tests

Open Wolemercy opened this issue 3 years ago • 1 comments

I'm trying to make use of an in-memory database (mongodb-memory-server) when running my tests, but I can't get it to work. I think that the fact that I'm requiring "loaders" when I create a server instance in the tests creates a mongoose connection with the actual database (mongoose.ts is one of the loaders). So I'm unable to connect with the test database.

I've gone through the "test-related" issues on this repo, and all of them seemed to make use of mocks, but I don't want to do that.

I'd appreciate any suggestions on how to resolve this---how to essentially, given this project structure, make use of a test database such as mongodb-memory-server.

Here's what my db-manager looks like - it houses the methods required to create the test DB.

import mongoose from "mongoose";
import { MongoMemoryServer }  from "mongodb-memory-server"

let mongod: MongoMemoryServer;

async function createMongoDB() {
  if (!mongod) {
    mongod = await MongoMemoryServer.create({
      binary: {
        version: "5.0.8"
      }
    })
  }
}

/**
 * Connect to the in-memory database.
 */
const connect = async () => {
  await createMongoDB()
  const uri = mongod.getUri()
  process.env.databaseURI = uri
  const connection = mongoose.connect(uri, { dbName: "riteshop-test-db"});
  return connection
}

/**
 * Clear the in-memory database.
 */
const clear = async () => {
  const collections = mongoose.connection.collections;
  for (const key in collections) {
    await collections[key].deleteMany({});
  }
};

/**
 * Disconnect from in-memory database.
 */
 const disconnect = async () => {
  await mongoose.connection.dropDatabase()
  await mongoose.connection.close()
  await mongod.stop()
}

export default { connect, clear, disconnect }

Also, here is what my test looks like

import request from "supertest"
import db from "../db-manager"
import config from "../../../src/config"
import express from "express"

describe("Health Check", () => {

  const app = express()
  beforeEach(async () => {
    await db.connect()
    // requiring the loaders is necessary because that's how the server is started normally
    // but that also means the db connection in `src/loaders/mongoose` would be initialized/attempted.
    // I don't want it attempted but it sorts of needs to be because the connection is needed by the dependency injector.
    await require("../../../src/loaders").default({ expressApp: app})
    app.listen(config.testPort)
  })
  
  afterEach(async () => {
    await db.clear()
    await db.disconnect()
  })
  
  describe('Endpoints availability', () => {
    it('should return 404', async () => {
      const res = await request(app).get('/api/nonexisting').send()
      expect(res.status).toEqual(404)
    })
  })

})

UPDATE: A few mins later

What I do now, which seems to work, is to

  • first, disconnect from the main db instance
  • then connect to the test db instance
const app = express()
  beforeEach(async () => {

    await require("../../../src/loaders").default({ expressApp: app})
    // disconecting from main DB
    mongoose.connection.close()
    // connecting to test DB
    await db.connect()
    app.listen(config.testPort)
  })

I'm hoping there's a much neater way to go about it. Furthermore, I'm not sure if this approach is consequence-free as the instance I had to disconnect from is what was passed to the dependencyInjectorLoader, and subsequently, the agendaInstance as seen in the repo's source code.

Wolemercy avatar Aug 19 '22 18:08 Wolemercy

Can't you use a different .env file for testing?

swizes avatar Jan 07 '23 01:01 swizes