angularunit-testingkarma-jasmineangular-unit-testangular-zoneless

Setup global providers for Angular unit tests


I have generated a fresh Angular 18 project and made it zoneless. It works fine, however in all of my unit tests I am now getting the following error:

Error: NG0908: In this configuration Angular requires Zone.js

I resolved the error by adding provideExperimentalZonelessChangeDetection() to each TestBed's providers, however it's quite annoying needing to add this manually to every spec file. Also it pops up again every time I generate a new component.

Is there a way to set up this provider globally for all unit tests? I have read that in the src/test.ts file it is possible to globally configure the tests, but in my project this file does not exist.


Solution

  • Apparently in v18, the test.ts file is not automatically generated anymore. However, it can be created manually.

    The next challenge is that initTestEnvironment() only accepts modules, no providers. To solve this issue, I created a module with a provider:

    import {
      NgModule,
      provideExperimentalZonelessChangeDetection,
    } from '@angular/core';
    import { getTestBed } from '@angular/core/testing';
    import {
      BrowserDynamicTestingModule,
      platformBrowserDynamicTesting,
    } from '@angular/platform-browser-dynamic/testing';
    
    @NgModule({
      providers: [provideExperimentalZonelessChangeDetection()],
    })
    class ZonelessModule {}
    
    getTestBed().initTestEnvironment(
      [BrowserDynamicTestingModule, ZonelessModule],
      platformBrowserDynamicTesting(),
    );
    

    Now this file needs to be registered in the angular.json via the main option:

    {
      "projects": {
        "my-project": {
          "architect": {
            // ...
            "test": {
              "builder": "@angular-devkit/build-angular:karma",
              "options": {
                "main": "src/test.ts",
                "tsConfig": "tsconfig.spec.json",
                "inlineStyleLanguage": "scss",
                "assets": [
                  {
                    "glob": "**/*",
                    "input": "public"
                  }
                ],
                "styles": ["src/styles.scss"],
                "scripts": [],
                "polyfills": ["@angular/localize/init"]
              }
            }
          }
        }
      }
    }
    

    Finally, the file needs to be added to to the files of the tsconfig.spec.json:

    {
      "extends": "./tsconfig.json",
      "compilerOptions": {
        "outDir": "./out-tsc/spec",
        "types": ["jasmine", "@angular/localize"]
      },
      "files": ["src/test.ts"],
      "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
    }