27

Jest runs your test suite in parallel by default, but there is a flag (--runInBand) that allows you to run the whole suite sequentially (as pointed out here)

I have some tests that cannot run in parallel, but running the whole suite sequentially takes a lot longer in total, so my question is if there is way to only run some tests that way (such as setting a flag for those tests or something similar).

Mario F
  • 45,569
  • 6
  • 37
  • 38
  • 1
    _"I have some tests that cannot run in parallel"_ - This usually hints tests aren't independent, a bigger problem... Or what you're doing isn't and shouldn't be a unit test. – Joseph Nov 14 '17 at 16:59
  • yeah, I am launching an external service for pact.js which is not that trivial to change. An acceptable compromise for me would be if I can isolate those tests and have them run sequentially without losing the speed for the whole suite – Mario F Nov 14 '17 at 17:01

4 Answers4

15

I too needed the same functionality. I have a large set of Jest integration test suites I want to run. However, some can't be run in parallel due to the need of setup and teardown of a shared resource. So, here is the solution I came up with.

I updated my package.json scripts from:

{
  ...
  "scripts": {
    ...
    "test": "npm run test:unit && npm run test:integration",
    "test:integration": "jest --config=__tests__/integration/jest.config.js",
    "test:unit": "jest --config=__tests__/unit/jest.config.js"
  },
  ...
}

to

{
  ...
  "scripts": {
    ...
    "test": "npm run test:unit && npm run test:integration",
    "test:integration": "npm run test:integration:sequential && npm run test:integration:parallel",
    "test:integration:parallel": "jest --config=__tests__/integration/jest.config.js",
    "test:integration:sequential": "jest --config=__tests__/integration/jest.config.js --runInBand",
    "test:unit": "jest --config=__tests__/unit/jest.config.js"
  },
  ...
}

Then I updated __tests__/integration/jest.config.js from

module.exports = {
  // Note: rootDir is relative to the directory containing this file.
  rootDir: './src',
  setupFiles: [
    '../setup.js',
  ],
  testPathIgnorePatterns: [
    ...
  ],
};

to

const Path = require('path');

const { defaults } = require('jest-config');
const klawSync = require('klaw-sync')
const mm = require('micromatch');

// Note: rootDir is relative to the directory containing this file.
const rootDir = './src';
const { testMatch } = defaults;

// TODO: Add the paths to the test suites that need to be run
// sequentially to this array.
const sequentialTestPathMatchPatterns = [
  '<rootDir>/TestSuite1ToRunSequentially.spec.js',
  '<rootDir>/TestSuite2ToRunSequentially.spec.js',
  ...
];

const parallelTestPathIgnorePatterns = [
  ...
];

let testPathIgnorePatterns = [
  ...parallelTestPathIgnorePatterns,
  ...sequentialTestPathMatchPatterns,
];

const sequential = process.argv.includes('--runInBand');
if (sequential) {
  const absRootDir = Path.resolve(__dirname, rootDir);
  let filenames = klawSync(absRootDir, { nodir: true })
    .map(file => file.path)
    .map(file => file.replace(absRootDir, ''))
    .map(file => file.replace(/\\/g, '/'))
    .map(file => '<rootDir>' + file);
  filenames = mm(filenames, testMatch);
  testPathIgnorePatterns = mm.not(filenames, sequentialTestPathMatchPatterns);
}

module.exports = {
  rootDir,
  setupFiles: [
    '../setup.js',
  ],
  testMatch,
  testPathIgnorePatterns,
};

The updated jest.config.js depends on jest-config, klaw-sync, and micromatch.

npm install --save-dev jest-config klaw-sync micromatch

Now, you can run npm run test:integration:sequential if you only want to run the tests that need to be run sequentially.

Or run npm run test:integration:parallel for the parallel tests.

Or run npm run test:integration to first run the sequential tests. Then when that is finished, the parallel tests will run.

Or run npm run test to run both the unit and integration tests.

Note: The directory structure I am using with my unit and integration tests is as follows:

__tests__
  integration
    src
      *.spec.js
      *.test.js
    jest.config.js
  unit
    src
      *.spec.js
      *.test.js
    jest.config.js
Danny Hurlburt
  • 674
  • 1
  • 8
  • 15
  • 3
    So much hassle for just quite a simple thing, Jest really lacks very important feature to mark describe block to be run sequential. – Vedmant Jan 29 '19 at 04:33
  • In the end it will run both suite sequentially if the first don't fail. Is this wanted? – Valentino Dell'Aica Oct 15 '20 at 13:31
  • in jest's defense - running tests with a worker pool is the smart thing. in our defense - clearing out a database before/after each test is also a smart thing to do and the two are completely at odds. thanks for the tip about the `--runInBand` flag! definitely saved me. all my tests started failing after I started doing proper cleanup before each test. – fIwJlxSzApHEZIl Sep 28 '21 at 19:55
3

Use the serial test runner:

npm install jest-serial-runner --save-dev

Set up jest to use it, e.g. in jest.config.js:

module.exports = {
   ...,
   runner: 'jest-serial-runner'
};

You can use the project feature to apply it only to a subset of tests. See https://jestjs.io/docs/en/configuration#projects-arraystring--projectconfig

Joachim Lous
  • 1,316
  • 1
  • 14
  • 22
  • 5
    To whoever voted this down: it would be more helpful to everyone if you commented what you thought was wrong or could be improved. – Joachim Lous Feb 25 '20 at 09:33
3

Extended from Joachim Lous's answer, you can divide test files into projects and specify a different runner for each project.

In jest.config.js:

module.exports = {
  projects: [
    {
      displayName: "default-tests",
      testEnvironment: "node",
    },
    {
      displayName: "serial-tests",
      testEnvironment: "node",
      runner: "jest-serial-runner",
      testMatch: ["**/?(*.)+(serial-test).[jt]s?(x)"],
    },
  ],
}

Then, rename any tests that need to be run sequentially to *.serial-test.js (as opposed to *.test.js).

Fishball
  • 241
  • 1
  • 2
  • 7
1

This was a bit of a lift, but I think it's worth posting my final config. I had this same problem and I extended Joachim Lous' and Fishball's answers to get to a satisfactory result. Here's my final setup:

An abbreviated directory listing of my project:

├── src
│   ├── index.ts
│   ├── Io.ts
│   ├── Modules
│   │   └── index.ts
│   └── .....
└── tests
    ├── EndToEnd
    │   ├── Fixtures.ts
    │   ├── Mq.spec.ts
    │   ├── LegalEntities.spec.ts
    │   └── Utils.ts
    └── UnitTests
        ├── LegalEntities.spec.ts
        ├── Assets.spec.ts
        └── Utils.ts

My (abbreviated) package.json file with my jest configs in it:

{
  "name": "my-project",
  "scripts": {
    "build": "scripts/build",
    "check": "tsc --noEmit",
    "test": "jest"
  },
  "....": "....",
  "jest": {
    "projects": [
      {
        "displayName": "unit-tests",
        "testEnvironment": "node",
        "verbose": true,
        "testMatch": [
          "<rootDir>/tests/**/*.spec.ts",
          "!**/EndToEnd/**/*.spec.ts"
        ],
        "transform": {
          "^.+\\.tsx?$": "ts-jest"
        }
      },
      {
        "displayName": "e2e-tests",
        "testEnvironment": "node",
        "verbose": true,
        "maxWorkers": 1,
        "testMatch": [
          "<rootDir>/tests/EndToEnd/**/*.spec.ts"   
        ],
        "transform": {
          "^.+\\.tsx?$": "ts-jest"
        }
      }
    ]
  }
}

Things to note:

  • When using the projects key in jest, I had to move all config into the individual project blocks. Using project config was mutually-exclusive with using global config.
  • I did not use the runner directive as mentioned in other answers. Instead I used the maxWorkers option to limit execution to 1 worker (i.e., inherently serial). This meant I didn't have to use more dependencies.
  • For some reason the negation syntax was finicky with my unit tests. I wanted to specify unit tests as all tests that were NOT in the EndToEnd directory, and it took me a few tried to get jest to do this correctly.

Thanks to everyone else for the viable starting point. Hope this helps others!

kael
  • 6,585
  • 5
  • 21
  • 27
  • 1
    Interestingly, this setup shouldn't work (and didn't for me). See this issue: https://github.com/facebook/jest/issues/10936 maxWorkers is ignored on the project level. – Narretz Feb 25 '21 at 11:12