200

I am setting a configuration to run my tests in a create-react-app + typescript app (from which I have ejected). I am using jest + enzyme. In my tsconfig.json I have set baseUrl='./src' so I can use absolute paths when I import modules. For example this is a typical import statement in one of my files:

import LayoutFlexBoxItem from 'framework/components/ui/LayoutFlexBoxItem';

You can see that the path is absolute (from /src folder) and not relative. This works fine when I run in debug mode ( yarn start )

But when I run my test ( yarn test ), I get this error:

 Cannot find module 'framework/components/Navigation' from 'index.tsx'

So it looks like jest is not able to resolve this absolute path although I have set it up in my tsconfig.json. This is my tsconfig.json:

{
  "compilerOptions": {
    "outDir": "dist",
    "module": "esnext",
    "target": "es5",
    "lib": ["es6", "dom"],
    "sourceMap": true,
    "allowJs": true,
    "jsx": "react",
    "moduleResolution": "node",
    "rootDir": "src",
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true,
    "baseUrl": "./src"    
  },
  "exclude": [
    "node_modules",
    "build",
    "dist",
    "config",    
    "scripts",
    "acceptance-tests",
    "webpack",
    "jest",
    "src/setupTests.ts"
  ]
}

Now I can see that there is a generated tsconfig.test.json at the root of my project. This is the ts configuration used for test. And here is its content:

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "module": "commonjs"
  }
}

As you can see the "module" is commonjs here whereas in the default configuration it is esnext. Could this be one reason?

Has any one been able to unit test his typescript project with Jest and absolute path? or is this a known bug? Since I have ejected from default configuration, are there some settings to put in my webpack configuration?

Thanks for your input and suggestion.

24 Answers 24

233

I was struggling with the same problem and actually it turns out that a simple change seems to do the trick.

I just updated the moduleDirectories field in jest.config.js.

Before

moduleDirectories: ['node_modules']

After

moduleDirectories: ['node_modules', 'src']
Sign up to request clarification or add additional context in comments.

8 Comments

I added this inside package.json, under jest.
This may be obvious, but if you include src path in imports like "... from 'src/...'", add this: moduleDirectories: ['node_modules', '.'] instead.
My issues was in a sub-project of an aws-cdk typescript project. My jest.config.ts had no moduleDirectories line, but when I added the line in your answer, it worked.
This worked for me but there's a lingering annoyance in Visual Studio Code where it still red-underlines the absolute imports in my tests. Any suggestions?
@redcartel add this line to your .vscode/settings.json or change the setting in the UI: "javascript.preferences.importModuleSpecifier": "non-relative"
|
109

As many here pointed out moduleNameMapper in jest.config.js needs to define paths specified in tsconfig.json. For example, if you have paths in tsconfig.json defined as follows

// tsconfig.json
{
 ...
 "baseUrl": "src",
 "paths": {
    "@alias/*": [ 'path/to/alias/*' ]
 }
 ...
}

then your jest.config.js needs to provide those paths in moduleNameMapper in the following format:

// jest.config.js
module.exports = {
    'roots': [
        '<rootDir>/src'
    ],
    'transform': {
        '^.+\\.tsx?$': 'ts-jest'
    },
    'moduleNameMapper': {
         '@alias/(.*)': '<rootDir>/src/path/to/alias/$1'
    }
};

Having that we can improve our jest.config.js to convert paths defined in tsconfig.json automatically. Here is a Gist code snippet for that:

// jest.config.js

function makeModuleNameMapper(srcPath, tsconfigPath) {
    // Get paths from tsconfig
    const {paths} = require(tsconfigPath).compilerOptions;

    const aliases = {};

    // Iterate over paths and convert them into moduleNameMapper format
    Object.keys(paths).forEach((item) => {
        const key = item.replace('/*', '/(.*)');
        const path = paths[item][0].replace('/*', '/$1');
        aliases[key] = srcPath + '/' + path;
    });
    return aliases;
}

const TS_CONFIG_PATH = './tsconfig.json';
const SRC_PATH = '<rootDir>/src';

module.exports = {
    'roots': [
        SRC_PATH
    ],
    'transform': {
        '^.+\\.tsx?$': 'ts-jest'
    },
    'moduleNameMapper': makeModuleNameMapper(SRC_PATH, TS_CONFIG_PATH)
};

5 Comments

Thanks. How does that cope with multiple locations defined for an alias (like "/opt/*": ["first/*", "second/*"])?
doesn'twork for me ` Could not locate module @/c/utils/http mapped as: `Update: works with only <rootDir> not <rootDir>/src
How about in a monorepo, where the project root is used to launch the tests, but the tsconfig and jest config are in one of the subdirs (such as frontend)? Still getting not found errors. I think it is related to <rootDir> resolving.
I like the makeModuleNameMapper function, but I don't think we should do that honestly. Just my personal opinion. The reason is that it is another code in the code base and even it is simple it may confuse some other developer who will deal with potential future issue. I'm for convention over configuration, but this is the config file.
Thank you so much! I've been struggling for hours with this
64

Here is how I got moduleNameMapper working.

With the below config in my tsconfig:

    "paths": {
      "@App/*": [
        "src/*"
      ],
      "@Shared/*": [
        "src/Shared/*"
      ]
    },

Here's the moduleNameMapper:

"moduleNameMapper": {
  "@App/(.*)": "<rootDir>/src/$1",
  "@Shared/(.*)": "<rootDir>/src/Shared/$1"
}

Comments

53

Add this following section in your package.json. after you made the changes don't forget to restart your test watchers.

  "jest": {
    "moduleDirectories": [
      "node_modules",
      "src"
    ],
    "moduleFileExtensions": [
      "js",
      "json",
      "ts"
    ],
    "roots": [
      "src"
    ],
    "testRegex": ".spec.ts$",
    "transform": {
      "^.+\\.(t|j)s$": "ts-jest"
    },
    "coverageDirectory": "../coverage",
    "testEnvironment": "node",
    "moduleNameMapper": {
      "src/(.*)": "<rootDir>/src/$1"
    }
  }

6 Comments

Worked for me when I was running into this issue with the nestjs framework. I made the edit to the package.json and am now able to run my tests with absolute paths.
After making the changes don't forget to restart your test watcher
As mentioned, if using NEST framework, this works wonderfully.
Is this still a working solution for you?
It works for me in create-react-app eject
|
30

For me, I just needed to add

"modulePaths": ["<rootDir>/src"],

to my jest.config.js file.

Following answer to modify moduleDirectories resulted in this error:

Jest encountered an unexpected token

Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

By default "node_modules" folder is ignored by transformers.

Here's what you can do:
 • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
 • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
 • If you need a custom transformation specify a "transform" option in your config.
 • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/configuration
For information about custom transformations, see:
https://jestjs.io/docs/code-transformation

Using:

modulePaths: ["node_modules", "<rootDir>/src"],

From reading the docs it appears that this a list of additional directories and so node_modules is unnecessary.

5 Comments

Unsure why this answer got downvoted - this is a clean answer that works perfectly with newer versions of jest at the very least (I'm on v27+ here) and is the recommended solution to this issue in the docs itself. The only thing is you don't actually need the <rootDir> part in most cases where your jest config is at your project root, src works fine too.
That's it, thank you been scratching my head for the last hour!
@xyzen Thank you ./src works as well but not src
Nice! For me it's "modulePaths": ["<rootDir>"], since I like to start my abs paths with src/
This is just my case, I dont use any alias. just the reduced version of ab path
23

For those using an absolute path but not using named mappings, this worked for me:

# jsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
  }
}

# jest.config.js
const config = {
  moduleDirectories: ['node_modules', '<rootDir>'],
};

2 Comments

This is what I needed
this is the right & simpler solution
12

Solution using the best practice

This error occurs because of using absolute paths in the import statements of our TypeScript/Nest.js/Angular projects while using Jest. Fixing it with moduleDirectories and moduleNameMapper options may solve your problem temporarily but it creates issues with other packages such as this TypeORM issue. Also, the creator of the Nest.js framework suggests here and here that using absolute paths is a bad practice. TypeScript's official documentation recommends using relative paths for our own modules.


Absolute path vs Relative path

import statement with absolute path looks like:

import { AuthService } from 'src/auth/auth.service'

import statement with relative path looks like:

import { AuthService } from '../auth/auth.service'

VS Code Setting

VS Code by default uses absolute path as shown above, when we auto-import using code completion or Command/Ctrl + .. We need to change this default setting to use relative paths.

Go to VS Code settings and search for a setting: Import Module Specifier. Change it from shortest to relative.

Now from here on, VS Code will automatically import using the relative paths.


Fixing imports in the project

Now in the project files, look for the absolute paths in the imports that look like the example above and delete them. You will see errors for the packages that you deleted. Simply use the auto-import suggestions and import them back. This time they will be imported using the relative paths. This step may be tedious depending on the size of your project but it's worth it in the long run.


Hope that works out! Cheers!

Comments

7

I use "baseUrl": "./" without any aliases in tsconfig.json

moduleDirectories: ['node_modules', '<rootDir>'] in jest.config.ts worked for me.

Now I can import local modules e.g import { Hello } from "src/modules/hello" without any problems.

jest.config.ts

/*
 * For a detailed explanation regarding each configuration property and type check, visit:
 * https://jestjs.io/docs/configuration
 */
export default {
  clearMocks: true,
  collectCoverageFrom: ['**/*.(t|j)s'],
  coverageDirectory: 'coverage',
  coverageProvider: 'v8',
  moduleDirectories: ['node_modules', '<rootDir>'],
  moduleFileExtensions: ['js', 'json', 'ts'],
  roots: ['src', 'test'],
  setupFiles: ['dotenv/config'],
  testRegex: ['.*\\.spec\\.ts$'],
  transform: {
    '^.+\\.(t|j)s$': 'ts-jest',
  },
};

tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "es2017",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "esModuleInterop": true,
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": false,
    "noImplicitAny": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false,
    "resolveJsonModule": true
  },
  "include": ["src/**/*", "src/**/*.json", "test/**/*"]
}

Comments

6

ts-jest can resolve this problem perfectly!
https://kulshekhar.github.io/ts-jest/docs/getting-started/paths-mapping#jest-config-with-helper
just modify jest.config.js like this:

    const { pathsToModuleNameMapper } = require('ts-jest/utils');
    const { compilerOptions } = require('./tsconfig.json');
    module.exports = {
        // preset is optional, you don't need it in case you use babel preset typescript
        preset: 'ts-jest',
        // note this prefix option
        moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, /* { prefix: '<rootDir>/' } */)
    }

Comments

5

Here is what worked for me:

npm i -D jest typescript
npm i -D ts-jest @types/jest
npx ts-jest config:init

Then in jest.config.js, here's my setup

module.exports = {
  preset: "ts-jest",
  testEnvironment: "node",
  modulePaths: ["node_modules", "<rootDir>/src"],
};

in my case, I do not have any paths in tsconfig.json but I have baseUrl set to src

Comments

5

If you have intalled ts-jest you can use an util function called pathsToModuleNameMapper to convert the path inside tsconfig.json to your jest.config file:

My jest.config.js file:

const { join } = require('path');
const { pathsToModuleNameMapper } = require('ts-jest')
const { compilerOptions } = require('./tsconfig.json')

/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */

module.exports = {
 rootDir: __dirname,
 setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
 setupFiles: ['<rootDir>/src/config/env.ts'],

 collectCoverageFrom: ["<rootDir>/src/modules/**/*UseCase.ts"],
 coverageProvider: "v8",
 coverageThreshold: {
   global: {
    lines: 40
   }
 },

 bail: true,
 clearMocks: true,
 displayName: 'unit-tests',
 testMatch: ["<rootDir>/src/modules/**/*.spec.ts"],

 preset: 'ts-jest',
 testEnvironment: 'node',

 modulePaths: ["<rootDir>/src"],
 moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
  prefix: join('<rootDir>', compilerOptions.baseUrl)
 })
};

2 Comments

Should use resetMocks not clearMocks
I was getting the error ` Cannot find module '~/routes' from 'src/server.ts'` in my integration test directory (outside my src dir) and this solution worked perfectly for me. Thanks
4

I've using React with Typescript, I removed react-scripts-ts test --env=jsdom from npm test and added jest --watch as my default test, after I added jest.config.js to my project following these instructions https://basarat.gitbooks.io/typescript/docs/testing/jest.html

and I used the the configuration mentioned by @Antonie Laffargue (add/edit property moduleDirectories: ['node_modules', 'src']), it works perfectly.

Comments

4

I had a similar problem. I hope this could help to spare time for some of you.

My problem:

  • using create-react-app with typescript
  • using absolute paths (src/MyComp) to import components inside other components (e.g. App.tsx)
  • it was working on compile/run/build
  • it was not working on test

I found that the error was due to a different value of the NODE_PATH. So I set it on tests run.

I recreated the issue and the fix in here: https://github.com/alessandrodeste/...

I'm not sure if this could bring side effects on tests. Let me know if you have feedback ;)

Comments

3

You probably want moduleNameMapper feature of jest config. It will map your custom import namespaces to real module locations.

see official documentation here:

https://facebook.github.io/jest/docs/en/configuration.html#modulenamemapper-object-string-string

4 Comments

Thanks for your answer. I tried to use moduleNameMapper like this: moduleNameMapper: { ".": "./src" } But now my test hangs with this message: Determining test suites to run... It looks like moduleNameMapper is used when I have mapped my paths. But in my case I have not mapped any path. I just specified a baseUrl
I'm having the same issue. Has anyone found a solution? I tried "moduleDirectories": ["src"], but it doesn't work.
A lot of try-hard here, but I found the solution with moduleNameMapper.
This one worked for me '^src/(.*)': '<rootDir>/src/$1',
3

Adding the following to my jest config in package.json resolved this problem for me.

 "moduleDirectories": [
  "node_modules",
  "src"
]

Comments

3

If this happens to you in monorepo here's what fixed the problem for me:

Inside jest.config.js

roots: ["<rootDir>packages"],
moduleNameMapper: {
  '@monopre/(.+)$': '<rootDir>packages/$1/src',
},

Assuming you have in tsconfig.json

"paths": {
  "@monopre/*": ["packages/*/src"],
}

Comments

3

On my side this worked for me:

I defined my paths in tsconfig.json:

"paths": { "@example/*": ["src/example/*"] }

After that, jest can't import this modules when a test is runned.

You must add in your jest.config.json the moduleNameMppaer:

"moduleNameMapper": {"^@example/(.*)$": [
        "<rootDir>/example/$1"
],}

This is the way that I had to follow.

Comments

2

In my case the problem was resolved using the moduleNameMapper parameter in jest.config.ts

TEST failed with error:

● Test suite failed to run

Cannot find module 'entities/*some_module*' from '*some_path*'

Added a line to the jest.config.ts file:

moduleNameMapper: {
    ...
    'entities/(.*)': "<rootDir>/src/entities/$1"
    ...
}

Project settings for this case:

tsconfig.json with absolut path

...
"baseUrl": ".",
"paths": {
    "*": [
        "./src/*"
    ]
},
...

Project tree:

...
src
 |- app
 |- entities
    |-*some_mudule*
       |- ...
       |- index.ts
 |- featues
 |- widgets
...
.jest.config.ts
.tsconfig.json
...

Comments

1

Using Svelte Kit, my solution was:

import { readFileSync } from 'fs';
import pkg from 'ts-jest/utils/index.js';
const { pathsToModuleNameMapper } = pkg;

const { compilerOptions } = JSON.parse(readFileSync('./tsconfig.json'))

export default {
    preset: 'ts-jest',
    testEnvironment: 'node',
    testMatch: ['<rootDir>/**/*.test.ts'],
    testPathIgnorePatterns: ['/node_modules/'],
    coverageDirectory: './coverage',
    coveragePathIgnorePatterns: ['node_modules'],
    globals: { 'ts-jest': { diagnostics: false } },
    transform: {},
    moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' }),
}

Comments

0

I had the same problem using StitchesJS, the module was not found, the solution was to put this in my jest.config.js

  moduleNameMapper: {
"stitches.config": "<rootDir>/node_modules/@stitches/react/dist/index.cjs"}

You can adapt according to the module you want.

Comments

0

If you have a monorepo where each project has its own tsconfig.json that extends from the base (root) tsconfig.json, and each project has its own jest.config.js, and you launch tests from the project root, you should add your moduleNameMapper to the jest.config.js in the project root.

Eg.

backend/
 - tsconfig.json
 - jest.config.js

frontend/
 - tsconfig.json
 - jest.config.js

/
- tsconfig.json
- jest.config.js

And your problem is in the jest tests frontend/ and it has a tsconfig like:

{
  "compilerOptions": {
    "jsx": "react-native",
    "paths": {
      "src/*": ["client/src/*"]
    },
    "noEmit": true
  },
  "extends": "../tsconfig.json"
}

Then adding this to the root jest.config should fix the issue:

  moduleNameMapper: {
    'src/(.*)': '<rootDir>/client/src/$1',
  },

So in total, something like:

module.exports = {
  testEnvironment: 'node',
  testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
  testPathIgnorePatterns: ['cdk.out/'],
  transform: {
    '^.+\\.tsx?$': 'ts-jest',
  },
  modulePathIgnorePatterns: ['tests'],
  globals: {
    'ts-jest': {
      isolatedModules: true,
    },
  },
  moduleNameMapper: {
    'src/(.*)': '<rootDir>/client/src/$1',
  },
};

Sort of obvious, but adding this answer since I didn't see this case in the other answers, and monorepos are still a thing :)

Comments

0

Kind of an old thread but recently, on top of the other suggestions of adding the paths on "moduleNameMapper": on jest.config.js and "paths": on tsconfig.json, I also had to include my test folder so the compiler could recognize it.

So on tsconfig.json, add:

...
"include": ["src", "__tests__"],
...

Hope it helps on someone googling for it 😉

2 Comments

This means that tests are included in the build artifacts. What if you want to use path aliases within jest tests but you do not want to include tests themselves in the build artifacts?
Actually never mind! I just answered my own question. The TypeScript compiler needs to be able to produce artifacts for these Jest files in order for path aliases to work (for whatever reason -- I'm not really sure why it can't hook into the "source code" artifacts). However, if the test files are excluded from the package.json "files" array then a package which depends upon the original package shouldn't see the test files anyway.
0

For me the problem was with the IDE. Actually, all you need to do is add

modulePaths: ['<rootDir>src'],

to your jest.config.ts, and then try to run it from terminal, not from IDE Run instrument. I don't relly know what the problem with my Webstorm, but from terminal it's running absolutely (no pun intended) fine

Comments

0

It may be stupid, but I stuck on that issue for some time and figured out, that the problem was in IDE, not in Jest. It's enoigh to add

 modulePaths: ['<rootDir>src'],

to your jest.config.js

But then try to run it from the terminal, not through IDE Run feature, if it works - then IDE is the problem. I'm using Webstorm and found a way to fix this issue, screenshots are down below

go to configs of your testing enviromnent

enter you path to config file here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.