Jonathan Cardoso Machado
[en] / pt-br
HOMEBLOG

Parallel testing a GraphQL server with Jest and MongoDB

graphqlmongodbjesttesting

This is a follow-up to the awesome post written by Sibelius Seraphini: Testing a GraphQL server using Jest

Previously here at Entria, we were running our tests using the --runInBand flag, which makes Jest work serially, running one test after the other.

This was needed because we were creating a physical temporary database to be used by all tests, and as so, we could not let two tests run at the same time, because it would create data inconsistencies between different tests. So the idea was basically to connect to the database before each test, populate the database and run the necessary tests, and after that drop the database, so it could be recreated again by the next test. Repeat this for the number of tests we had.

We solved this thanks to the awesome library mongodb-memory-server by @nodkz: https://github.com/nodkz/mongodb-memory-server

To use it, we created our own jest test environment, which is just a single file:

// eslint-disable-next-line import/no-extraneous-dependencies
const NodeEnvironment = require('jest-environment-node')
const MongodbMemoryServer = require('mongodb-memory-server')

class MongoDbEnvironment extends NodeEnvironment {
  constructor(config) {
    super(config)
    // eslint-disable-next-line new-cap
    this.mongod = new MongodbMemoryServer.default({
      instance: {
        // settings here
        // dbName is null, so it's random
        // dbName: MONGO_DB_NAME,
      },
      binary: {
        version: '3.6.1',
      },
      // debug: true,
    })
  }

  async setup() {
    await super.setup()
    // console.log('\n# MongoDB Environment Setup #');

    this.global.__MONGO_URI__ = await this.mongod.getConnectionString()
    this.global.__MONGO_DB_NAME__ = await this.mongod.getDbName()
    // this is used to have different names for documents created while testing
    this.global.__COUNTERS__ = {
      user: 0,
    }
  }

  async teardown() {
    // console.log('\n# MongoDB Environment Teardown #');
    await super.teardown()
    await this.mongod.stop()
  }

  runScript(script) {
    return super.runScript(script)
  }
}

module.exports = MongoDbEnvironment

And configured jest to use it, by setting the testEnvironment setting:

//...
  "jest": {
    "testEnvironment": "<rootDir>/test/environment/mongodb",
    "resetModules": true,
    //...

Note the resetModules setting there, it’s also important if you use Mongoose, otherwise, it will try to reuse the same connection from the previous test that was run. This happens because we are setting our models using the default connection from mongoose, which is stored directly on the exported mongoose object.

And that is it, you can see it in action on the boilerplate we created here at Entria, here is the PR adding it: https://github.com/entria/graphql-dataloader-boilerplate/pull/80

before
after

On one of our biggest projects:

before real project
before
after real project
after
…


Made with
using Gatsby