Repository navigation
unit-test builder with vitest is slow for testing and for code coverage #31724
Copy link
Copy link
Open
Labels
angular/build:unit-testarea: @angular/buildfreq1: lowOnly reported by a handful of users who observe it rarelyOnly reported by a handful of users who observe it rarelyneeds: investigationRequires some digging to determine if action is neededRequires some digging to determine if action is neededseverity4: memory/performance
Description
Command
test
Is this a regression?
- Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
No response
Description
I migrated a very small existing project using the jasmine-to-vitest schematic and code coverage is fairly slow when using vitest (roughly 80 tests).
With the karma builder:
ng test --no-watch 9,99s user 1,71s system 214% cpu 5,459 total
ng test --no-watch --code-coverage 17,82s user 2,42s system 232% cpu 8,719 total
When migrates to the unit-test builder (and a few tests disabled):
ng test --no-watch 18,49s user 3,33s system 259% cpu 8,396 total
ng test --no-watch --coverage 38,46s user 4,66s system 210% cpu 20,535 total
The tests themselves takes 18s (10s with Karma), and the code coverage takes 20s instead of 8s. This is very noticeable when using vitest.
Minimal Reproduction
This is probably reproducible in a new CLI project, by generating a bunch of components and comparing test times.
Exception or Error
Your Environment
Angular CLI : 21.0.0-rc.1
Angular : 21.0.0-rc.1
Node.js : 22.18.0
Package Manager : npm 10.9.0
Operating System : darwin arm64
┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/build │ 21.0.0-rc.1 │ 21.0.0-rc.1 │
│ @angular/cli │ 21.0.0-rc.1 │ 21.0.0-rc.1 │
│ @angular/common │ 21.0.0-rc.1 │ 21.0.0-rc.1 │
│ @angular/compiler │ 21.0.0-rc.1 │ 21.0.0-rc.1 │
│ @angular/compiler-cli │ 21.0.0-rc.1 │ 21.0.0-rc.1 │
│ @angular/core │ 21.0.0-rc.1 │ 21.0.0-rc.1 │
│ @angular/forms │ 21.0.0-rc.1 │ 21.0.0-rc.1 │
│ @angular/localize │ 21.0.0-rc.1 │ 21.0.0-rc.1 │
│ @angular/platform-browser │ 21.0.0-rc.1 │ 21.0.0-rc.1 │
│ @angular/router │ 21.0.0-rc.1 │ 21.0.0-rc.1 │
│ rxjs │ 7.8.2 │ 7.8.2 │
│ typescript │ 5.9.3 │ 5.9.3 │
│ vitest │ 4.0.8 │ 4.0.8 │
Anything else relevant?
Of course this is maybe an upstream problem, but I thought it was worth mentioning it.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
angular/build:unit-testarea: @angular/buildfreq1: lowOnly reported by a handful of users who observe it rarelyOnly reported by a handful of users who observe it rarelyneeds: investigationRequires some digging to determine if action is neededRequires some digging to determine if action is neededseverity4: memory/performance
Activity
[-]`unit-test` builder with `vitest` is slow for code coverage[/-][+]`unit-test` builder with `vitest` is slow for testing and for code coverage[/+]clydin commentedon Nov 7, 2025
Vitest's test isolation improves reliability of tests but does have a cost. Also the file parallelism allows tests suites to be run in parallel but has a startup time cost per file.
Additionally, for code coverage, Vitest uses an AST based remapping system that provides excellent results but which can add to coverage analysis time.
Can you provide the breakdown of build vs test times for each scenario? Also relevant, builds will cache intermediate information by default which can greatly affect the overall process time. If not disabled, it might be useful to do so for profiling purposes.
Also, DOM emulation (jsdom/etc.) does also have a startup time cost. There are tradeoffs there especially when considering size of each test file and amount of test files.
You should also be able to experiment with some of these options via a custom runner config (
runnerConfigoption) similar to the following (vitest-base.config.ts):cexbrayat commentedon Nov 8, 2025
I forgot to mention that I used
ChromiumHeadlessfor the vitest runner.Here are the build/test/coverage time.
Build took ~3.5s in all cases.
Karma:
Vitest (ChromiumHeadless):
Vitest (ChromiumHeadless with
isolate: false) throws withNG0400as @jnizet mentioned below, I can retry when the issue is fixed.Vitest (ChromiumHeadless with
fileParallelism: false):I don't see a difference with this option.
Is there something else I can try/provide?
jnizet commentedon Nov 8, 2025
Some additional numbers and information that might be interesting.
I ran a test on an app where I made 1000 copies of the same component (a form) (without removing the other ones). That makes 9139 tests in total.
This app was originally tested with Karma / Jasmine.
I rewrote the tests to use Vitest browser mode (playwright), with the Vitest browser API to interact with locators, retried assertions ans so on. So of course those tests do more than the original tests: they query by role and text, interact with the fields in a more realistic way, etc. I expect them to be a bit slower. But I consider these to be the best practices.
All tests (Karma and Vitest) are run in headless mode (ChromeHeadless for Karma, ChromiumHeadless for Vitest).
When I ran the original Jasmine tests with Karma, it took 17 min. and 18 seconds. But something was odd: the firsts tests go very fast (1000 tests in 23 seconds), but in the end, everything was very slow. The culprit was the default reporter configured by the CLI, which, I guess, clutters the DOM of the testing page with more and more elements.
Running the same tests with the progress reporter takes 3 minutes and 24 seconds (testing time only, reported by Karma).
Running the tests migrated to Vitest browser mode, on my laptop (MacBook Pro, 64 GB RAM, Intel 8 cores), takes 7 min. 18 seconds. That's really really slower, considering that Vitest runs the tests in parallel, and my machine has 8 cores.
This shows when running the tests with ``fileParallelism: false`: it takes 30 minutes and 17 seconds.
Trying to run them with
isolate: falsecrashes the execution after 1000 tests (or so) with the following error:Trying to run them with
isolate: falseandfileParallelism: falsecrashes the execution after 1 test with the same error:Given that tests run fine with Karma without any isolation, I guess they should run without isolation in Vitest too, but that doesn't seem to be the case (see the error above).
Ideally, Vitest could run the tests in parallel (in N different iframes), but without isolation in each of these iframes. Then I guess we could have tests that take about the same time as with Karma, while still benefitting from the advantages and more realistic behavior of Vitest.
Addendum:
I migrated the duplicated test to Vitest, without switching to locators, i.e. keeping basically the same code as before. Running the 9009 tests with Vitest with
fileParallelism: falseleads to an execution time of 5 min. 27 seconds.This shows that isolation, in browser mode, is costly (around 60% of overhead).
If Angular could make it possible to run tests without isolation (as in Karma), it would have a really nice effect on the test execution time.
yjaaidi commentedon Nov 8, 2025
It is indeed way more performant without isolation. Iframe reload costs around 100ms per test file.
@clydin, I don't remember where I mentioned this but the init should be ran once in the virtual init file with something like:
Workaround
In the meantime, it works with the analog plugin and we get really outstanding performance.
https://analogjs.org/docs/features/testing/vitest
cexbrayat commentedon Nov 9, 2025
I retried with
isolate: falseand I have the same issue 👍yjaaidi commentedon Nov 9, 2025
It's fixed here: #31731
In the mean time you could also add a test setup file with the following workaround:
Note that this workaround will be a tiny bit slower than the fix as this recreates a new test bed env for each test file
yjaaidi commentedon Nov 9, 2025
There is an issue where setup files end up using dependencies duplicate modules when using Vitest with the CLI. This means two instances of
TestBed. #31732This only happens in browser mode and the workaround is to mark such dependencies as external dependencies:
angular.jsonand add the following option"externalDependencies": ["@angular/core", "@angular/core/testing"]unit-testconfiguration to use"buildTarget": "build:test"cexbrayat commentedon Nov 14, 2025
Even if rc.3 fixed a bunch of issues, it's still not possible to test
isolate: falseas it throwsyjaaidi commentedon Nov 14, 2025
Oh! This PR should have fixed that: #31792 but it wasn't shipped in rc3.
@clydin is there a specific reason or the idea is to only support isolated mode for 21.0.0?
jnizet commentedon Nov 14, 2025
Just tested with the rc4, and
isolate: falseworks flawlessly now. Good job guys.Thank you @clydin and @yjaaidi
hybrist commentedon Nov 15, 2025
Sorry, that was my bad yesterday. I forgot to check for merge-ready RC PRs before running the release scripts. Glad to hear it's working now! 🎉
cexbrayat commentedon Nov 15, 2025
After updating to rc.4 which solved various issues and uses
isolate: trueby default, the unit-test builder with vitest gives the following results:For comparison, the karma builder gives:
It's still slowish compared to Karma but now usable for code coverage.
Using
isolate: false(default in next rc):makes it a bit faster.
GipHub123 commentedon Nov 20, 2025
I tried running my test cases with Vitest with ng-mocks, but I had previously run them successfully using Karma. I was using official release 21.0.0
Log was filled with weird error like.
Vitest seems quite slow—it took around 27 minutes, whereas the same tests ran in about 6-7 minutes with Karma.
Note that I'm not sure if problems are ng-mocks / vitest / jsdom or test related.
Although failing test cases contribute to the longer runtime, the overall performance is still slow.
yjaaidi commentedon Nov 20, 2025
Hi @GipHub123,
@angular/build@21.0.0or an RC?Error: Component '_XXXComponent' has unresolved metadata.seems like an AOT issue. Did you try turning onaotoption in Karma? It should produce the same error.AFAIK, there is currently no way of using full JIT with the CLI runner. The current workaround is to use the Analog plugin https://analogjs.org/docs/features/testing/vitest
happy-dominstead ofjsdomwhich should be a bit faster too.environmentduration, it seems that isolation is enabled and that can drastically slow down your tests.collectphase is also a bit slow. Do you have lots of code running outside tests (i.e.it&test) or hooks (e.g.beforeEachetc...`) ?GipHub123 commentedon Nov 21, 2025
@yjaaidi Thanks for replying 👍
I didn't try browser mode. I've not used happy-dom or jsdom before but I get a feeling from various discussions that jsdom is more stable and a bit slower than happy-dom. I guessed that some of the errors might be related to jsdom. Others have reported about similar errors
ng g @schematics/angular:refactor-jasmine-vitest+ manually fixed a few remaining errors when I tried vitest.And yes I noticed that some of the errors could be fixed with a temporary hack. For example
export default defineConfig({ test: { pool: 'vmThreads', } });Using these suggests that Vitest still has some rough edges that need polishing before it’s stable enough for production use with Angular.
arobinson commentedon Jan 23, 2026
I'm just up-taking Vitest in our project. In JSDom, the coverage report is generated very fast. With
ChromiumHeadless, one test completes in 13 seconds, but the coverage report takes 4 minutes to create after the test completes.Checking the lcov.info file, it is instrumenting 590 files, so a large portion of our application. But even with
karma-coverageeven though it had memory issues, it was much faster to create the reports.Running 1 test in ChromiumHeadless with coverage enabled:
From the tests that we have supporting JSDom, running 2 tests with coverage enabled:
We are doing two runs, one with some tests in jsdom and another set of tests running in ChromiumHeadeless (using
.test.tsfor jsdom and.browser.test.tsfor chromium). The idea is once we are off Karma and onto Vitest then we can worry about getting 100% of our tests working in jsdomfix(@angular/build): favor istanbul coverage provider when browser te…
fix(@angular/build): favor istanbul coverage provider when browser te…
fix(@angular/build): favor istanbul coverage provider when browser te…
GipHub123 commentedon Sep 6, 2026
I recently migrated from Karma to Vitest using Angular’s default configuration. All the problems seem to be fixed, and I no longer have any performance issues 👍