{"id":33075,"date":"2026-04-02T17:32:55","date_gmt":"2026-04-02T15:32:55","guid":{"rendered":"https:\/\/www.angulararchitects.io\/blog\/migrate-from-karma-to-vitest\/"},"modified":"2026-04-03T10:14:41","modified_gmt":"2026-04-03T08:14:41","slug":"migrate-from-karma-to-vitest","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/","title":{"rendered":"Migrate from Karma To Vitest"},"content":{"rendered":"<p><strong>Contents<\/strong><\/p>\n<p><a href=\"#why-vitest-instead-of-karma?\">Why Vitest instead of Karma?<\/a><br \/>\n<a href=\"#more-on-vitest\">More on Vitest<\/a><br \/>\n<a href=\"#before-starting-the-migration\">Before starting the migration<\/a><br \/>\n<a href=\"#confirm-you\u2019re-using-the-application-build-system\">Confirm you\u2019re using the application build system<\/a><br \/>\n<a href=\"#decide-on-the-migration-strategy\">Decide on the migration strategy<\/a><br \/>\n<a href=\"#check-your-current-behavior\">Check your current behavior<\/a><br \/>\n<a href=\"#migration-steps-for-angular-cli-workspaces\">Migration steps for Angular CLI workspaces<\/a><br \/>\n<a href=\"#update-to-angular-21\">Updating Angular<\/a><br \/>\n<a href=\"#remove-karma\">Remove Karma<\/a><br \/>\n<a href=\"#install-dependencies-and-adjust-configs\">Install dependencies and adjust configs<\/a><br \/>\n<a href=\"#use-a-custom-vitest-config\">Use a custom Vitest config<\/a><br \/>\n<a href=\"#migrate-existing-test-files\">Migrate existing test files<\/a><br \/>\n<a href=\"#prompts\">Prompts<\/a><br \/>\n<a href=\"#\\(optional\\):-run-unit-tests-in-the-browser\">(Optional): Run unit tests in the browser<\/a><br \/>\n<a href=\"#notes-for-nx-workspaces\">Notes for Nx Workspaces<\/a><br \/>\n<a href=\"#conclusion\">Conclusion<\/a><br \/>\n<a href=\"#resources\">Resources<\/a><\/p>\n<p><a id=\"why-vitest-instead-of-karma?\"><\/a><\/p>\n<h2>Why Vitest instead of Karma?<\/h2>\n<p>For years, the \u201cdefault Angular testing stack\u201d meant Jasmine + Karma: Jasmine provided the test API (<code>describe<\/code>\/<code>it<\/code>\/<code>expect<\/code>), while Karma acted as the runner that bundled your code and executed tests inside a real browser. That approach worked well historically, but it also accumulated friction: launching browsers is comparatively heavy, and keeping browsers available and consistent is a recurring pain point in CI environments.<\/p>\n<p>If you visit Karma\u2019s official GitHub page, you can read that Karma is now deprecated and does not accept new features or general bug fixes anymore.<\/p>\n<p>At the same time, the wider JavaScript ecosystem has shifted towards faster, more developer-friendly tooling. Vitest runs tests in a Node.js process using a DOM emulation layer (typically <code>happy-dom<\/code> or <code>jsdom<\/code>), so you can test DOM-oriented code without launching a browser.<\/p>\n<p>In late 2025, the Angular team announced that Vitest is the new default test runner for new Angular projects, and that Vitest support in the CLI is now considered \u201cstable and production-ready\u201d.<\/p>\n<p>Long story short: Karma is deprecated and Vitest is the future.<\/p>\n<p><a id=\"more-on-vitest\"><\/a><\/p>\n<h2>More on Vitest<\/h2>\n<p>As already mentioned, Vitest typically runs in a DOM emulation layer. <code>happy-dom<\/code> is known for its performance, while <code>jsdom<\/code> is known for its robustness and its ability to reflect the real browser API more closely. It is still possible that certain browser APIs are not fully implemented, and you may have to add or adjust mock functions for those in your tests.<\/p>\n<p>It\u2019s worth mentioning that Vitest can still run in a real browser when you need it, via browser mode (e.g., using a Playwright-backed provider). In Angular\u2019s migration guide, browser mode is positioned as an optional step: you install a provider (such as @vitest\/browser-playwright) and set a <code>browsers<\/code> option in <code>angular.json<\/code>.<\/p>\n<p>The Angular team\u2019s own \u201cwhy\u201d (as stated publicly) is essentially this: modernize testing, reduce heavyweight browser coupling when it\u2019s not required, and provide a better developer experience\u2014without losing the ability to do real-browser testing when it is required.<\/p>\n<p><a id=\"before-starting-the-migration\"><\/a><\/p>\n<h2>Before starting the migration<\/h2>\n<p><strong>Note:<\/strong> The migration experience is still evolving. The Angular docs describe migrating an existing project to Vitest as experimental and tie it to the newer application build system. In practice, that means you should plan the migration like any other tooling change: iteratively, with a safety net.<\/p>\n<p><a id=\"confirm-you\u2019re-using-the-application-build-system\"><\/a><\/p>\n<h3><strong>Confirm you\u2019re using the application build system<\/strong><\/h3>\n<p>Angular\u2019s official migration guide notes that migrating to Vitest requires the application build system, which is the default for newly created projects. Existing projects can migrate to that build system (optionally) via the documented build-system migration process.<\/p>\n<p>This matters because the Vitest-based unit testing integration is implemented through the newer Angular build tooling and builders.<\/p>\n<p><a id=\"decide-on-the-migration-strategy\"><\/a><\/p>\n<h3><strong>Decide on the migration strategy<\/strong><\/h3>\n<p>Because Angular still supports Karma\/Jasmine, you can choose between:<\/p>\n<p><strong>Big-bang migration:<\/strong> convert everything to Vitest, delete Karma, done.<br \/>\n<strong>Progressive migration:<\/strong> keep Karma running while you introduce Vitest, migrate test suites over time, then remove Karma once the Vitest suite is consistently green.<\/p>\n<p>Progressive migration is often lower risk in real organizations, particularly for large or compliance-sensitive codebases, because it keeps a known-good runner available during refactors.<\/p>\n<p>If you decide to take this strategy, you have to keep the Karma npm dependencies and the config files. The config files have to be adjusted to include only the required <code>.spec.ts<\/code> files so they do not overlap with the Vite configs. Coverage reports also have to be handled separately in this case for Karma and Vitest.<\/p>\n<p><a id=\"check-your-current-behavior\"><\/a><\/p>\n<h3><strong>Check your current behavior<\/strong><\/h3>\n<p>Before touching the configuration, note any Karma-specific features you rely on\u2014custom reporters, coverage thresholds, browser launchers, or polyfills injected via the Karma builder. The Angular migration guide explicitly warns that custom <code>karma.conf.js<\/code> settings are not automatically migrated, so you should audit them before deletion.<\/p>\n<p><a id=\"migration-steps-for-angular-cli-workspaces\"><\/a><\/p>\n<h2>Migration steps for Angular CLI workspaces<\/h2>\n<p>This section generalizes the required steps to fully migrate from Karma to Vitest, which works across typical Angular CLI applications. Read through the steps carefully, and adjust them to meet your project-specific requirements if needed.<\/p>\n<p><a id=\"update-to-angular-21\"><\/a><\/p>\n<h3><strong>Update Angular<\/strong><\/h3>\n<p>An update to the latest version is recommended; otherwise, the migration might get stuck on different issues. Based on the official Angular guide:<\/p>\n<ul>\n<li>Run <code>ng update @angular\/core@21 @angular\/cli@21<\/code> \u2014 do not use <code>--force<\/code> while installing npm packages.<\/li>\n<li>Install version <code>^21.0.0<\/code> of other third-party Angular libs if possible (e.g. <code>@ng-select\/ng-select<\/code>, <code>ngx-markdown<\/code>, etc.).<\/li>\n<li>Run the build, the linter, and existing unit tests to confirm everything works as before. Manually opening the running application is also recommended.<\/li>\n<\/ul>\n<p><a id=\"remove-karma\"><\/a><\/p>\n<h3><strong>Remove Karma<\/strong><\/h3>\n<p>If you go with the full migration strategy, remove typical Karma-related files and dependencies.<\/p>\n<p>Uninstall typical dependencies:<\/p>\n<pre><code class=\"language-bash\">npm uninstall karma karma-chrome-launcher karma-coverage-istanbul-reporter karma-jasmine karma-jasmine-html-reporter karma-junit-reporter jasmine-core jasmine-spec-reporter karma-coverage @types\/jasmine @types\/jasminewd2 --save<\/code><\/pre>\n<p><strong>Note:<\/strong> Compare this with your own dependencies, extend it if needed, and execute it selectively.<\/p>\n<p>Remove related files and their references from:<\/p>\n<ul>\n<li><code>src\/karma.conf.js<\/code><\/li>\n<li><code>src\/test.ts<\/code><\/li>\n<li><code>tsconfig.spec.json<\/code><\/li>\n<\/ul>\n<p>Remove <code>&quot;karma&quot;<\/code> from <code>compilerOptions.types<\/code> in any <code>tsconfig.json<\/code> or <code>tsconfig.spec.json<\/code> files.<\/p>\n<p><a id=\"install-dependencies-and-adjust-configs\"><\/a><\/p>\n<h3><strong>Install dependencies and adjust configs<\/strong><\/h3>\n<p>Install new dependencies:<\/p>\n<pre><code class=\"language-bash\">npm install --save-dev vitest@^4.0.0 jsdom @vitest\/coverage-istanbul<\/code><\/pre>\n<p>Explanation:<\/p>\n<ul>\n<li><code>vitest<\/code> (at least version 4 is recommended; otherwise, you might run into issues)<\/li>\n<li><code>jsdom<\/code> or <code>happy-dom<\/code> according to your choice<\/li>\n<li>Custom coverage reporter if needed (Istanbul in my case)<\/li>\n<\/ul>\n<p>Add @angular\/build:unit-test as the new builder for your unit tests by adjusting <code>angular.json<\/code>:<\/p>\n<pre><code class=\"language-json\">{\n  &quot;projects&quot;: {\n    &quot;your-project-name&quot;: {\n      &quot;architect&quot;: {\n        &quot;test&quot;: {\n          &quot;builder&quot;: &quot;@angular\/build:unit-test&quot;,\n          &quot;options&quot;: {\n            &quot;tsConfig&quot;: &quot;tsconfig.spec.json&quot;\n          }\n        }\n      }\n    }\n  }\n}<\/code><\/pre>\n<p>Add <code>&quot;vitest\/globals&quot;<\/code> to <code>compilerOptions.types<\/code> in <code>tsconfig.json<\/code>.<\/p>\n<p>Change <code>tsconfig.spec.json<\/code> to include <code>polyfills.ts<\/code>, either in the <code>files<\/code> array or in the <code>include<\/code> array.<\/p>\n<p>If you used the <code>--code-coverage<\/code> flag while running your unit tests, it has to be replaced with <code>--coverage<\/code> due to API changes in <code>ng test<\/code>. Similarly, the <code>--browsers<\/code> option has to be removed as long as you rely on <code>jsdom<\/code> or <code>happy-dom<\/code>.<\/p>\n<p>From this point, we are set up and can execute <code>ng test<\/code> and run Vitest. Of course, it is going to run into several issues, as we haven\u2019t migrated the tests themselves yet. That is something we are going to fix in the next chapters. But before that, we\u2019re going to extend the <code>test.options<\/code> object with a <code>&quot;runnerConfig&quot;: &quot;vitest-ng.config.ts&quot;<\/code> option to use a custom Vitest config.<\/p>\n<p><a id=\"use-a-custom-vitest-config\"><\/a><\/p>\n<h3><strong>Use a custom Vitest config<\/strong><\/h3>\n<p>Vitest projects typically contain a <code>vitest.config.ts<\/code> file at the root of the project, which tells Vitest how to run the tests\u2014which files are included, where the setup files are, which coverage reporter should be used, etc.<\/p>\n<p>As we have seen, Angular runs Vitest without such a file, which also causes some issues when it comes to third-party tools, like using Vitest IDE extensions to run and debug your tests directly from the editor. In this case, the extensions are looking for a <code>vitest.config.ts<\/code> file, but since they do not find one, they are clueless and fail to run our Angular tests. This is a known issue in the community, and Younes Jaaidi already addressed the problem in this Angular GitHub issue:<br \/>\n<code><a href=\"https:\/\/github.com\/angular\/angular-cli\/issues\/31734\">https:\/\/github.com\/angular\/angular-cli\/issues\/31734<\/a><\/code><\/p>\n<p>The good news is that we are going to implement a solution for it right here.<\/p>\n<p>First, Angular actually provides a <code>&quot;runnerConfig&quot;<\/code> option, which makes it possible to feed the builder with a standard Vitest config file.<\/p>\n<p>A typical <code>vitest.config.ts<\/code> file in the root would look something like this:<\/p>\n<pre><code class=\"language-ts\">import path from &#039;node:path&#039;;\nimport { defineConfig, ViteUserConfig } from &#039;vitest\/config&#039;;\n\nexport const baseConfig: ViteUserConfig = {\n  resolve: {\n    alias: {\n      src: path.resolve(__dirname, &#039;src&#039;)\n    }\n  },\n  test: {\n    globals: true,\n    environment: &#039;jsdom&#039;,\n    setupFiles: [&#039;@angular\/localize\/init&#039;, &#039;.\/src\/app\/test-utils\/test-setup.ts&#039;],\n    coverage: {\n      provider: &#039;istanbul&#039;,\n      reporter: [&#039;lcovonly&#039;, &#039;html&#039;],\n      reportsDirectory: &#039;.\/coverage&#039;\n    }\n  }\n};\n\nexport default defineConfig(baseConfig);<\/code><\/pre>\n<p>You can see that we define how the paths should be resolved, which environment (<code>jsdom<\/code>) we are using, how coverage should be configured, and what setup files we need.<\/p>\n<p><strong>Note:<\/strong> <code>test-setup.ts<\/code> should look something like this to initialize the Angular test environment:<\/p>\n<pre><code class=\"language-ts\">import { TestBed } from &#039;@angular\/core\/testing&#039;;\nimport { BrowserTestingModule, platformBrowserTesting } from &#039;@angular\/platform-browser\/testing&#039;;\n\nTestBed.initTestEnvironment(BrowserTestingModule, platformBrowserTesting());<\/code><\/pre>\n<p>This solution makes our config compatible with typical editor plugins and makes it possible to run and debug our unit tests granularly directly in the editor.<\/p>\n<p>However, if we feed this config to our Angular builder, it is going to complain that <code>testEnvironment<\/code> has already been called.<\/p>\n<p>We want to keep <code>vitest.config.ts<\/code> as the single source of truth, so we create an extended version\u2014let\u2019s call it <code>vitest-ng.config.ts<\/code>\u2014and override the <code>setupFiles<\/code> like this:<\/p>\n<pre><code class=\"language-ts\">import { defineConfig } from &#039;vitest\/config&#039;;\nimport baseConfig from &#039;.\/vitest.config&#039;;\n\nexport default defineConfig({\n  ...baseConfig,\n  test: {\n    ...baseConfig.test,\n    setupFiles: []\n  }\n});<\/code><\/pre>\n<p>Now we can feed this into Angular by adjusting our <code>angular.json<\/code>:<\/p>\n<pre><code class=\"language-json\">{\n  &quot;projects&quot;: {\n    &quot;your-project-name&quot;: {\n      &quot;architect&quot;: {\n        &quot;test&quot;: {\n          &quot;builder&quot;: &quot;@angular\/build:unit-test&quot;,\n          &quot;options&quot;: {\n            &quot;tsConfig&quot;: &quot;tsconfig.spec.json&quot;,\n            &quot;runnerConfig&quot;: &quot;vitest-ng.config.ts&quot;\n          }\n        }\n      }\n    }\n  }\n}<\/code><\/pre>\n<p>Now we can edit <code>vitest.config.ts<\/code> whenever we want to adjust general settings, and it is going to be consumed by our editor plugins and <code>ng test<\/code> as well.<\/p>\n<p><a id=\"migrate-existing-test-files\"><\/a><\/p>\n<h3><strong>Migrate existing test files<\/strong><\/h3>\n<p>Angular provides a migration script called <code>refactor-jasmine-vitest<\/code>, which is being improved over time, but according to the official migration guide, it is still experimental, meaning we cannot expect it to perform a 100% complete migration. I\u2019m going to go through the issues I found after running the script and also give some advice on how to get past them.<\/p>\n<p>First, before running the script, make sure your repository is in a clean, committed state.<\/p>\n<p>Then execute the script:<\/p>\n<pre><code class=\"language-bash\">npx ng g @schematics\/angular:refactor-jasmine-vitest<\/code><\/pre>\n<p>I also advise committing all the changes to preserve them.<\/p>\n<p>After running the migration, I found that it ignored a few syntax changes (e.g. <code>toBeTrue<\/code> does not exist anymore; <code>toBeTruthy<\/code> is the successor), it did not get rid of <code>waitForAsync<\/code> and <code>fakeAsync<\/code> methods (<code>zone.js<\/code>), and it had issues with files that had a <code>.spec.ts<\/code> extension but did not contain any tests.<\/p>\n<p>The most efficient way to solve the remaining issues in batch was to select an AI agent tool (like Copilot in VS Code), select a decent model (GPT-5.3-Codex at this time), and execute a few detailed prompts that pointed out the issues clearly.<\/p>\n<p><div style=\"\nmargin: 8px 0;\npadding: 22px;\nborder: 1px solid #e5e7eb;\nborder-radius: 14px;\nbackground: #f8fafc;\n\">NOTE<\/p>\n<h3 style=\"margin-top:0\">Modern Angular<\/h3>\n<p>More about Signal Forms can be found in my new book <a href=\"https:\/\/www.angulararchitects.io\/en\/ebooks\/modern\/\">Modern Angular - Architecture, Concepts, Implementation<\/a>. This book covers everything you need for building modern business applications with Angular: from Signals and state patterns to architecture, AI assistants, testing, and practical solutions for real-world projects.<\/p>\n<p><a href=\"https:\/\/www.angulararchitects.io\/en\/ebooks\/modern\/\"><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/01\/cover-klein.png\" width=\"400\" alt=\"Modern Angular - Architecture, Concepts, Implementation\" style=\"cursor:pointer !important\"><\/a><\/p>\n<p><a style=\"cursor:pointer !important\" href=\"https:\/\/www.angulararchitects.io\/en\/ebooks\/modern\/\">Learn more about the book \u2192<\/a>\n<\/div>\n<\/p>\n<p><a id=\"prompts\"><\/a><\/p>\n<h3>Prompts<\/h3>\n<ul>\n<li>Search for all the <code>.toBe<\/code>, <code>.toBeTruthy<\/code>, and <code>.toBeFalsy<\/code> methods where they get 2 params, and move the 2nd param to the previous <code>expect<\/code> method as a second param in all <code>.spec.ts<\/code> files.<\/li>\n<li>Wherever I use <code>waitForAsync<\/code> and <code>TestBed.configureTestingModule<\/code>, change the <code>waitForAsync<\/code> to a simple <code>async<\/code> method instead and await the <code>TestBed.configureTestingModule<\/code>.<\/li>\n<li>Rename all the <code>.spec.ts<\/code> files to <code>.ts<\/code> files that do not contain a test suite, and move them to <code>src\/app\/test-utils\/**<\/code>.<\/li>\n<li>Remove empty spec files.<\/li>\n<li>Find <code>.spec.ts<\/code> files that use <code>fakeAsync<\/code> and avoid using it due to migration to Vitest.<\/li>\n<\/ul>\n<p><strong>Important:<\/strong><\/p>\n<ul>\n<li>Execute the prompts one by one.<\/li>\n<li>Always review all the changes and make adjustments (or even revert) if needed.<\/li>\n<li>Committing changes between prompts is also helpful.<\/li>\n<\/ul>\n<p>Inserting this extra note helps a lot:<\/p>\n<pre><code class=\"language-bash\">ng test --watch=false<\/code><\/pre>\n<p>to check the results and iterate if needed.<\/p>\n<p>In the end, the tests should complete, and all the code changes should be understood.<\/p>\n<p><a id=\"(optional):-run-unit-tests-in-the-browser\"><\/a><\/p>\n<h3>(Optional): Run unit tests in the browser<\/h3>\n<p>At this point, you should already be able to run all your unit tests using Vitest. \ud83c\udf89<br \/>\nHowever, there is still some room for improvement when it comes to the test environment and performance.<\/p>\n<p>As long as your Vitest setup relies on environments such as <code>jsdom<\/code> or <code>happy-dom<\/code>, it relies on lightweight DOM implementations. They are easy to set up and work well for most test cases, but they are still simulations, not real browsers. An alternative approach is to run your unit tests inside a real browser engine using Playwright. Vitest provides first-class support for this through the @vitest\/browser-playwright package. This allows tests to run in a fully implemented browser environment (for example, Chromium), which can improve both compatibility and performance.<\/p>\n<p>I ran a quick benchmark on a test project:<\/p>\n<ul>\n<li>\n125 test files\n<\/li>\n<li>\n619 tests total\n<\/li>\n<\/ul>\n<p>Average pure test execution time:<\/p>\n<ul>\n<li><code>jsdom<\/code>: <strong>23.31 seconds<\/strong><\/li>\n<li>Playwright (headless Chromium): <strong>16.83 seconds<\/strong><\/li>\n<\/ul>\n<p>This resulted in roughly a <strong>28% performance improvement<\/strong>, which is quite significant for larger test suites.<\/p>\n<p>In order to switch from <code>jsdom<\/code> to Playwright\u2019s Chromium, you have to do the following:<\/p>\n<p>First, install the Playwright integration for Vitest:<\/p>\n<pre><code class=\"language-bash\">npm install --save-dev @vitest\/browser-playwright<\/code><\/pre>\n<p>Then update the Vitest configuration in <code>vitest.config.ts<\/code>:<\/p>\n<pre><code class=\"language-ts\">\/\/ Import the Playwright provider\nimport playwright from &#039;@vitest\/browser-playwright&#039;;\n\nexport default defineConfig({\n  test: {\n    \/\/ Replace the jsdom environment with the browser configuration.\n    browser: {\n      provider: playwright(),\n      enabled: true,\n      headless: true,\n      instances: [\n        { browser: &#039;chromium&#039; }\n      ]\n    }\n  }\n});<\/code><\/pre>\n<p>With this configuration, tests run inside a real headless Chromium browser, and the environment behaves much closer to how Angular code runs in production. You may also see noticeable performance improvements.<\/p>\n<p><a id=\"notes-for-nx-workspaces\"><\/a><\/p>\n<h2>Notes for Nx Workspaces<\/h2>\n<p>Migration from Karma to Vitest works in Nx monorepos more or less similarly to how it works in Angular CLI projects, with a few differences.<\/p>\n<p>The biggest difference is that the migration script provided by Angular\u2014<code>refactor-jasmine-vitest<\/code>\u2014won\u2019t work due to the different project structure. In practice, that means that the list of prompts provided previously has to be extended with a few extra steps according to the thrown errors, mostly for handling syntax changes.<\/p>\n<p>The next difference is that an Nx workspace doesn\u2019t have a single <code>angular.json<\/code> file responsible for all of your projects (apps and libs), but instead defines a <code>project.json<\/code> file for each of the projects you have, which defines the test executor as well. Here, in <code>project.json<\/code>, you can set <code>test.executor<\/code> to <code>angular\/build:unit-test<\/code> in order to use the Vitest runner provided by Angular. Also, in <code>test.options.runnerConfig<\/code>, you can provide an Angular- and project-specific <code>vitest-ng.config.ts<\/code> file to pass your configs to the runner.<\/p>\n<p>It\u2019s important not to place <code>vitest.config.ts<\/code> files at project level, as Vitest plugins and extensions are going to automatically detect them, and they are not very good at resolving the path to project-specific tests. So the working version was, in terms of extension compatibility, to put a single <code>vitest.config.ts<\/code> file at the root of the Nx workspace, with the same settings as previously mentioned, listing <code>setupFiles<\/code> with a global <code>test-setup.ts<\/code> which calls <code>initTestEnvironment<\/code> for Angular compatibility.<\/p>\n<p><a id=\"conclusion\"><\/a><\/p>\n<h2>Conclusion<\/h2>\n<p>For many years, Karma + Jasmine served Angular developers well. However, the ecosystem has evolved. With Karma now officially deprecated and Angular CLI adopting Vitest as the default testing solution for new projects, the direction of the platform is clear.<\/p>\n<p>Migrating from Karma to Vitest is more than just swapping one test runner for another, but the migration process is manageable when approached methodically:<\/p>\n<ul>\n<li>First, ensure the project runs on a modern Angular version and build system.<\/li>\n<li>Replace the Karma infrastructure with Vitest dependencies and builder configuration.<\/li>\n<li>Introduce a custom Vitest configuration so both Angular and editor tooling can work seamlessly.<\/li>\n<li>Gradually refactor existing Jasmine-based tests to Vitest-compatible syntax.<\/li>\n<li>Use automation tools, migration scripts, or AI-assisted prompts to handle repetitive refactoring tasks efficiently.<\/li>\n<\/ul>\n<p>Once the migration is complete, the benefits become clear: performance, continuous updates, and better IDE integration. These improvements make the testing experience noticeably smoother for Angular developers.<\/p>\n<p>It\u2019s also worth noting that the ecosystem around Vitest continues to evolve quickly. Tooling support, editor integrations, and Angular-specific utilities are improving steadily. This means that migrating today not only solves the Karma deprecation issue, but also positions your project to take advantage of future improvements in the Angular testing landscape.<\/p>\n<p>In short: Vitest is not just a replacement for Karma\u2014it is the foundation for Angular\u2019s next generation of testing workflows.<\/p>\n<p><a id=\"resources\"><\/a><\/p>\n<h2>Resources<\/h2>\n<ul>\n<li><a href=\"https:\/\/angular.dev\/guide\/testing\/migrating-to-vitest\">https:\/\/angular.dev\/guide\/testing\/migrating-to-vitest<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/angular\/angular-cli\/issues\/31734\">https:\/\/github.com\/angular\/angular-cli\/issues\/31734<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/karma-runner\/karma\">https:\/\/github.com\/karma-runner\/karma<\/a><\/li>\n<li><a href=\"https:\/\/nx.dev\/docs\/technologies\/test-tools\/vitest\/introduction\">https:\/\/nx.dev\/docs\/technologies\/test-tools\/vitest\/introduction<\/a><\/li>\n<\/ul>\n<p><\/p>\n<p><em><a href=\"https:\/\/www.linkedin.com\/in\/marcellkiss\/\">Marcell Kiss<\/a> is a frontend architect and freelance consultant in the DACH region, with deep expertise in Angular and modern web architectures. He focuses on advancing developer workflows and applying LLMs and agentic systems to real-world frontend solutions.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Contents Why Vitest instead of Karma? More on Vitest Before starting the migration Confirm you\u2019re using the application build system Decide on the migration strategy Check your current behavior Migration steps for Angular CLI workspaces Updating Angular Remove Karma Install dependencies and adjust configs Use a custom Vitest config Migrate existing test files Prompts (Optional): [&hellip;]<\/p>\n","protected":false},"author":36,"featured_media":33067,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[18],"tags":[],"class_list":["post-33075","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Migrate from Karma To Vitest - ANGULARarchitects<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Migrate from Karma To Vitest - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"Contents Why Vitest instead of Karma? More on Vitest Before starting the migration Confirm you\u2019re using the application build system Decide on the migration strategy Check your current behavior Migration steps for Angular CLI workspaces Updating Angular Remove Karma Install dependencies and adjust configs Use a custom Vitest config Migrate existing test files Prompts (Optional): [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2026-04-02T15:32:55+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-04-03T08:14:41+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/As-Karma-is-Deprecated-1024x536.png\" \/>\n<meta name=\"author\" content=\"Marcell Kiss\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/As-Karma-is-Deprecated-1024x536.png\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Marcell Kiss\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/\"},\"author\":{\"name\":\"Marcell Kiss\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/c7e514881f538ff35efe69eff43e55b2\"},\"headline\":\"Migrate from Karma To Vitest\",\"datePublished\":\"2026-04-02T15:32:55+00:00\",\"dateModified\":\"2026-04-03T08:14:41+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/\"},\"wordCount\":2433,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2562282845.jpg\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/\",\"name\":\"Migrate from Karma To Vitest - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2562282845.jpg\",\"datePublished\":\"2026-04-02T15:32:55+00:00\",\"dateModified\":\"2026-04-03T08:14:41+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2562282845.jpg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2562282845.jpg\",\"width\":1000,\"height\":545},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Migrate from Karma To Vitest\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"name\":\"ANGULARarchitects\",\"description\":\"AngularArchitects.io\",\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\",\"name\":\"ANGULARarchitects\",\"alternateName\":\"SOFTWAREarchitects\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"width\":644,\"height\":216,\"caption\":\"ANGULARarchitects\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/github.com\/angular-architects\",\"https:\/\/www.linkedin.com\/company\/angular-architects\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/c7e514881f538ff35efe69eff43e55b2\",\"name\":\"Marcell Kiss\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/596017d406ba412f57947f13c70160bcf6853ea2711a96ef3f80bb830d895066?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/596017d406ba412f57947f13c70160bcf6853ea2711a96ef3f80bb830d895066?s=96&d=mm&r=g\",\"caption\":\"Marcell Kiss\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Migrate from Karma To Vitest - ANGULARarchitects","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/","og_locale":"en_US","og_type":"article","og_title":"Migrate from Karma To Vitest - ANGULARarchitects","og_description":"Contents Why Vitest instead of Karma? More on Vitest Before starting the migration Confirm you\u2019re using the application build system Decide on the migration strategy Check your current behavior Migration steps for Angular CLI workspaces Updating Angular Remove Karma Install dependencies and adjust configs Use a custom Vitest config Migrate existing test files Prompts (Optional): [&hellip;]","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/","og_site_name":"ANGULARarchitects","article_published_time":"2026-04-02T15:32:55+00:00","article_modified_time":"2026-04-03T08:14:41+00:00","og_image":[{"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/As-Karma-is-Deprecated-1024x536.png","type":"","width":"","height":""}],"author":"Marcell Kiss","twitter_card":"summary_large_image","twitter_image":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/As-Karma-is-Deprecated-1024x536.png","twitter_misc":{"Written by":"Marcell Kiss","Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/"},"author":{"name":"Marcell Kiss","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/c7e514881f538ff35efe69eff43e55b2"},"headline":"Migrate from Karma To Vitest","datePublished":"2026-04-02T15:32:55+00:00","dateModified":"2026-04-03T08:14:41+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/"},"wordCount":2433,"commentCount":0,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2562282845.jpg","inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/","name":"Migrate from Karma To Vitest - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2562282845.jpg","datePublished":"2026-04-02T15:32:55+00:00","dateModified":"2026-04-03T08:14:41+00:00","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2562282845.jpg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2562282845.jpg","width":1000,"height":545},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/migrate-from-karma-to-vitest\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Migrate from Karma To Vitest"}]},{"@type":"WebSite","@id":"https:\/\/www.angulararchitects.io\/en\/#website","url":"https:\/\/www.angulararchitects.io\/en\/","name":"ANGULARarchitects","description":"AngularArchitects.io","publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.angulararchitects.io\/en\/#organization","name":"ANGULARarchitects","alternateName":"SOFTWAREarchitects","url":"https:\/\/www.angulararchitects.io\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","width":644,"height":216,"caption":"ANGULARarchitects"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/github.com\/angular-architects","https:\/\/www.linkedin.com\/company\/angular-architects\/"]},{"@type":"Person","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/c7e514881f538ff35efe69eff43e55b2","name":"Marcell Kiss","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/596017d406ba412f57947f13c70160bcf6853ea2711a96ef3f80bb830d895066?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/596017d406ba412f57947f13c70160bcf6853ea2711a96ef3f80bb830d895066?s=96&d=mm&r=g","caption":"Marcell Kiss"}}]}},"_links":{"self":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/33075","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/users\/36"}],"replies":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/comments?post=33075"}],"version-history":[{"count":10,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/33075\/revisions"}],"predecessor-version":[{"id":33097,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/33075\/revisions\/33097"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/33067"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=33075"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=33075"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=33075"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}