The nice thing about Jest is it's easy to get started. However, most tutorials tell you about setting up Jest in a default Angular project. I was interested in using Jest in a monorepo, so I had to research.
As far as I can see, there are five roads you can go:
- Starting with Angular 18 (or even later?), you can use the native support Angular offers. I'm covering this in another article, but as of Angular 17, I don't think it's ready for use in production. Experiment with it and tell the Angular team about it because that's how open source progresses, but don't use it in production.
- You can configure Jest manually. That's what this article is about.
- You can use the Briebug schematics to set up Jest in your Angular project or workspace.
- You can use a plugin like the "Just Jeb" builder (aka @angular-builders/jest. I'm covering this here. You may also want to read Jeb's article.
- You can use nx. That's almost certainly a good option, but until now, I have always shied away from using nx. I don't want to add another dependency until I need it. That's why I can't tell you much about it.
Setting up the monorepo
Before setting up Jest, let's look at the project structure.
Angular is an opinionated framework, so I set up my monorepo the Angular way. Probably the most future-proof option is using the Angular CLI commands. Using a workspace with subprojects sounds like the way to go:ng new my-workspace --create-application=false cd my-workspace ng generate application app1 ng generate application app2 ng generate library my-favorite-library
Linking the library to your node_modules folder
Now, you can start implementing code in your library and using it in the applications. Most of the time, this works without further ado. However, I learned it's a good idea to treat the library like a proper library you publish to and download from npm. In particular, Jest requires you to do so.
Most other tutorials tell you to add paths to the
tsconfig.json file and
module_mappers to the Jest configuration, but that's tedious and poorly documented. Using
npm link is easier.
In my projects, I've added a couple of scripts to the
package.json to make the library available to the apps:
postinstallscript automatically runs after each
build libis pretty much standard. It compiles the shared library. Angular (and
ng-packagr) stores the binaries in a subfolder of the
- Finally, there's another
postscript that's called after each
npm run build:lib.It makes the compiled binaries of the library available to the apps.
If you want to dive deeper, read my article about
Shortcut: use briebug/jest-schematic
You can save a lot of work by using an Angular schematic:ng add @briebug/jest-schematic
After doing so, you can skip over most of this article and continue with the "migration" section. The following few paragraphs describe how to set up Jest manually. They also provide additional insight that might interest you.
Setting up Jest for the command line
If you prefer to configure Jest manually, the first step is installing Jest and its Angular preset:npm install jest @types/jest jest-preset-angular --save-dev
If you want to run Jest from the command line, you'll also have to install Jest globally:npm install -g jest
You can only run Jest from an npm script without the global installation. Which version you prefer is a matter of taste. A side-effect of our monorepo is you always run Jest with a long list of parameters, so I like to store the command as a script in the
Uninstall Karma and Jasmine
If your installation contains Karma and Jasmine, you can now remove both. By default, Angular 17 doesn't install any test framework. So, this step is primarily an issue if you've got an older project.npm uninstall karma karma-chrome-launcher karma-coverage-istanbul-reporter karma-jasmine karma-jasmine-html-reporter karma-coverage jasmine-core rm karma.config.js rm src/test.ts
You can also remove the
test section from the
angular.json. Jest doesn't read the
angular.json, so this configuration part is obsolete.
After a lengthy trial and error, I learned there's no way to use a single Jest configuration for the entire monorepo. You have to configure Jest for every sub-project of your workspace. This also means you can't run every test with a single command. You have to run Jest for every sub-project individually.
Add the file
setup-jest.ts to the root folder of your workspace:
Now, add a file called
refers to the sub-project folder. So we need to add or modify the
tsconfig.spec.json in each sub-project:
If your workspace already has such a file, you only need to replace
"jest" in the
Disgression: why do we need the setup-jest.ts?
Why did we add two files? If you've read the
jest-config.js carefully, you've probably noticed that the
setup-jest.ts is explicetely referenced in that file. So it's not surprising you don't need this file. In fact, the briebug schematic doesn't add this file to your workspace. You can add the Angular preset in the
jest-config.js just as well:
setup-jest.ts file is handy for defining global mocks. It's executed before your tests run. For example, I'm using this feature in one of my projects to disable logging to the console. That's probably an exotic use-case: my project logs too many messages to the console. It messes up the messages of Jest. Monkey-patching
window.console fixed that for me:
Adding the Jest scripts to the package.json
Now you can run Jest. It gets easier if you add the lengthy commands as scripts to the
Now you can run the tests with a slightly shorter command like
npm run test:app1.
Error messages at the import statements
If Jest reports an error at the
import statement, check your
tsconfig.*files module settings. Usually, this means Jest tries to use the CommonJS module system instead of the ESM module system. These settings worked for me:
Remains IDE support. If you're using Webstorm, that's easy: you're already there. You don't have to configure anything.
The same holds for Jest Runner. That simple plugin allows you to run individual tests or test suites with a single click. Debugging works out of the box, too.
In an earlier article, I described Orta's Jest plugin. This one is more capricious. It has an option to set up monorepos. It's possible to configure it - I managed it once - but after a while, I abandoned it in favor of the more straightforward plugin.
Wrapping it up
Setting up Jest in a monorepo is easy. All you have to know is you must configure Jest for every sub-project. There's no common configuration covering the entire workspace.
I've published a GitHub repository showing you several Jest setups, including monorepos and traditional projects.