Colocation

Contents

Optionally place tests next to your source code file.

Intro

So far, all tests have been placed under src/jest, cleanly separated from the source code. It is possible to place the tests in the same directory as the source code, but it has some drawbacks and complexity.

We will cover it all in this chapter.

Jest configuration

In order for Jest to find and run the colocated tests, the test files must be included in the testMatch configuration. This can be done by adding the colocated test files as include patterns in the Jest configuration file:

jest.config.mjs
const DIR_SRC_ASSETS = `${DIR_SRC}/assets`;

const clientSideConfig = {
  // ...
  testMatch: [
    // ... other testMatches
    `<rootDir>/${DIR_SRC_ASSETS}/${AND_BELOW}/${TEST_FILES}`,
  ],
};

const serverSideConfig = {
  // ...
  testMatch: [
    // ... other testMatches
    `<rootDir>/${DIR_SRC}/${AND_BELOW}/${TEST_FILES}`,
  ],
  testPathIgnorePatterns: [
    `<rootDir>/${DIR_SRC_ASSETS}/`,
  ],
};

Build system

So far, we have been using tsdown to build the sources. Its configuration discovers every source file with glob and turns each of them into an entry. If the tests are placed in the same directories as the source code, the build system will process the tests as source code. This is not what we want. We want the build system to ignore the tests.

This can be done by passing the test files as ignore patterns to the entries() helper in the tsdown configuration file:

tsdown.config.ts
const TEST_FILES = '**/*.{spec,test}.{ts,tsx}';

const serverEntry = entries(SRC, '{ts,js}', [`${SRC_ASSETS}/**`, `${SRC}/**/*.d.ts`, `${SRC}/${TEST_FILES}`]);
const assetEntry = entries(SRC_ASSETS, '{tsx,ts,jsx,js}', [`${SRC_ASSETS}/${TEST_FILES}`]);

Type checking

Test files can contain things like jest functions in the global scope that are not available in the source code. This will cause the type checker to fail.

When running type checks these tsconfig.json files are used:

  • src/main/resources/tsconfig.json

  • src/main/resources/assets/tsconfig.json

, while these give the editor its type context for the test files:

  • src/jest/client/tsconfig.json

  • src/jest/server/tsconfig.json

Each of them brings its own include and exclude patterns.

In the current configuration include patterns with *.ts and *.tsx are used.

Obviously those patterns also match test files ending with *.test.ts or *.test.tsx.

Here is how to make sure test files a type checked as test files, not as source files:

Exclude from source code type checking

src/main/resources/tsconfig.json
{
  // ... other settings
  "exclude": [
    // ... other excludes
    "**/*.spec.ts",
    "**/*.test.ts",
    "**/*.spec.tsx",
    "**/*.test.tsx"
  ]
}
src/main/resources/assets/tsconfig.json
{
  // ... other settings
  "exclude": [
    // ... other excludes
    "./**/*.spec.ts",
    "./**/*.test.ts",
    "./**/*.spec.tsx",
    "./**/*.test.tsx"
  ]
}

Include in test code type checking

src/jest/client/tsconfig.json
// ... other settings
  "include": [
    // ... other includes
    "../../main/resources/assets/**/*.spec.ts",
    "../../main/resources/assets/**/*.spec.tsx",
    "../../main/resources/assets/**/*.test.ts",
    "../../main/resources/assets/**/*.test.tsx",
  ]
src/jest/server/tsconfig.json
// ... other settings
  "include": [
    // ... other includes
    "../../main/resources/**/*.spec.ts",
    "../../main/resources/**/*.spec.tsx",
    "../../main/resources/**/*.test.ts",
    "../../main/resources/**/*.test.tsx",
  ],
  "exclude": [
    // ... other excludes
    "../../main/resources/assets/**/*.*",
  ]

IDE

Typically, IDEs use the closest tsconfig.json to resolve import paths and global types.

Which means that a test file colocated with source code is treated as source code by the IDE.

There may be a couple solutions to this:

Adding support for test syntax to all source files.

This is a bad option, because it may cause runtime problems if a developer adds test syntax in a source file, and get no warnings while coding, type checking or building.

In test files: Use relative imports and avoid globals.

This can be achieved by importing jest globals directly from @jest/globals.

/src/main/resources/**/any.test.ts
import {
  afterAll,
  afterEach,
  beforeAll,
  beforeEach,
  describe,
  expect,
  jest,
  test as it,
  // ...
} from '@jest/globals';

Summary

This wraps up the tutorial, we hope you enjoyed it. Visit the tutorials section on our Developer Portal for more.


Contents

Contents