Logo
Published on

How to speed up Jest tests

Authors
  • Name
    Twitter

TDD is awesome. It changes the way we are thinking about programming, it solves tremendous number of problems. However, every test takes time and memory. It is problem I was struggling a lot. Here I will show you couple solutions you should consider having the same problem.

How to check memory usage in jest?

just add flag : ‘ — logHeapUsage’

//package.json
"scripts":  {
"test":  "jest --logHeapUsage ",
}

Solution 1 — ISOLATED_MODULES

If you use TypeScript tests can take a bit longer since Jest will check types. Actually you can speed this up by skipping types checking.

Open your ‘jest.config.ts’ file (or file you use for jest configuration)

Inside find property ‘transform’

const config: JestConfigWithTsJest = {
  transform: {
    "^.+\\.(t|j)s$": ["ts-jest", { isolatedModules: true }],
  },
};

When you add { isolatedModules:true } tests will run a bit faster.

Solution 2 — Check your globalSetup and setupFilesAfterEnv

Open your ‘jest.config.ts’ file (or file you use for jest configuration)

There are 2 properties :

const config: JestConfigWithTsJest = {
  // many more properties
  globalSetup: "./test/bootstrap.ts",
  setupFilesAfterEnv: ["./test/setup.ts"],
  // many more properties
};
  • globalSetup will run every time you run ‘npm run test’. In my case it is ‘./test/bootstrap.ts’ file. So it will run function from there once.
  • setupFilesAfterEnv will fire before each test. So if you have 1000 tests, it can run 1000 times after your ‘npm run test’

Now open file from setupFilesAfterEnv and check for unnecessary things there. Some can be move to global setup, everything will work faster.

// setup.ts , so my setupFilesAfterEnv file
afterAll(async () => {
  await truncate();
  redis.quit();
});
// setup.ts , so my globalSetup file
export default async () => {
  //most of those lines were in setupFilesAfterEnv berfore
  global._request = request;

  const moduleFixture: TestingModule = await Test.createTestingModule({
    imports: [AppModule],
  }).compile();

  global.app = moduleFixture.createNestApplication();

  setContainer(app);
  setGlobalPrefix(app);
  initI18(app);

  await global.app.init();

  global.request = request(app.getHttpServer());

  global.i18nService = i18nService;
  global.prismaService = app.get<PrismaService>(PrismaService);
  global.graph = graph;

  await truncate();
};

Actually, in my case (E2E tests)I can leave methods for truncating database, in setupFilesAfterEnv, rest I moved to globalSetup.

Solution 3 — expose-gc

It helped a lot when it comes to memory usage. On my Mac tests are a bit slower but I think it can speed up tests on slow machines/test servers

//package.json
"scripts":  {
"test:ci":  "node --expose-gc ./node_modules/.bin/jest --logHeapUsage --ci --runInBand",
}

Just add ‘ — expose-gc’ flag. You can also try ‘ — ci’ and ‘ — runInBand’, very often it helps when you use CI

Solution 4— Running only some test (group runner)

Sometimes we need to run only some tests. I wrote article how to do this using jest group runner. You can read more here

Solution 5— in memory db

Some ORMs can support db in memory, so without hiting hard disk. In can increase performance a lot, especially in E2E tests, however you can face some problems with migrations. Not every ORM supports inMemory DB, but here you have code for sequalize:

// sequalize example
let sequelize;
if (process.env.NODE_ENV === "test") {
  sequelize = new Sequelize("sqlite::memory:");
}