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-libraryLinking 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:
- The
postinstall
script automatically runs after eachnpm install.
build lib
is pretty much standard. It compiles the shared library. Angular (andng-packagr
) stores the binaries in a subfolder of the/dist
folder.- Finally, there's another
post
script that's called after eachnpm 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 npm-link
.
Shortcut: use briebug/jest-schematic
You can save a lot of work by using an Angular schematic:
ng add @briebug/jest-schematicAfter 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-devIf you want to run Jest from the command line, you'll also have to install Jest globally:
npm install -g jestYou 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 package.json
.
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.tsYou can also remove the test
section from the angular.json.
Jest doesn't read the angular.json,
so this configuration part is obsolete.
Configure Jest
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 config-jest.js
to the root folder of every project. Note that this file has to be a JavaScript file (as opposed to the setup-jest
file, which may be both JavaScript or TypeScript). Here we go:
Note that
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 "jasmine"
with "jest"
in the types
array.
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:
However, the 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 package.json
:
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:
IDE support
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.