Skip to content

V8 Coverage showing unexpected results #32836

@daniemo2

Description

@daniemo2

Which @angular/* package(s) are the source of the bug?

Don't known / other

Is this a regression?

No

Description

I'm in the process of migrating from jasmine/karma with istanbul coverage to vitest with v8 coverage. I'm currently migrating around 2500 unit tests. With Istanbul I had over 95% coverage across statements, branches, functions and lines. With vitest I'm seeing lower coverage, mainly on the branches with 90%.

I've noticed that even empty classes (template only) show 90% branch coverage (9/10).

A minimal reproduction:

  • Create a new Angular 21 application: ng new ng21
  • Install v8 coverage npm i --save-dev @vitest/coverage-v8
  • Remove the protected title variable from app.ts -- protected readonly title = signal('ng21');
  • Remove the should render title test from app.spec.ts
  • Run ng test --coverage

This is the coverage I see:
Image

Similar from my project:
Image

with the spec file as:

import { TestBed } from '@angular/core/testing';
import { HeaderComponent } from './header.component';
import { HeaderComponent as ClsHeaderComponent, TenantDropdownComponent } from '@cls/ui-util';
import { MockComponent } from '@cls/ui-util/testing';
import { SearchComponent } from '../../schedule/components/search/search.component';

describe('HeaderComponent', () => {
    beforeEach(async () => {
        await TestBed.configureTestingModule({
            imports: [HeaderComponent],
            providers: []
        })
            .overrideComponent(HeaderComponent, {
                remove: { imports: [SearchComponent, ClsHeaderComponent, TenantDropdownComponent] },
                add: {
                    imports: [
                        MockComponent({ selector: 'app-search' }) as any,
                        MockComponent({ selector: 'cls-header' }) as any,
                        MockComponent({ selector: 'cls-tenant-dropdown' }) as any
                    ]
                }
            })
            .compileComponents();
    });

    function setup() {
        const fixture = TestBed.createComponent(HeaderComponent);

        return {
            fixture,
            app: fixture.componentInstance,
            compiled: fixture.debugElement.nativeElement
        };
    }

    it('should create the app', () => {
        const { app } = setup();
        expect(app).toBeTruthy();
    });
});

angular.json

"test": {
  "builder": "@angular/build:unit-test",
  "options": {
    "browsers": ["chromium"],
    "buildTarget": "admin-ui:build",
    "headless": true,
    "tsConfig": "./tsconfig.spec.json",
    "runnerConfig": "./vitest.config.ts",
    "coverage": true,
    "coverageInclude": ["src/app/**/*.ts"],
    "coverageExclude": ["index.ts", "main.ts", "polyfills.ts", "**/*.routes.ts", "**/*.model.ts", "**/*.interface.ts"],
    "coverageReporters": ["text", "html", "lcov"],
    "coverageThresholds": {
      "statements": 90,
      "branches": 90,
      "functions": 90,
      "lines": 90
    }
  }
}

vitest.config.ts

import { defineConfig } from 'vitest/config';

export default defineConfig({
    test: {
        environment: 'jsdom',
        globals: true,
        setupFiles: ['src/test-setup.ts'],
        restoreMocks: true,
        coverage: {
            provider: 'v8'
        },
        browser: {
            screenshotFailures: false
        }
    }
});

Have I misconfigured something? Do I have an incorrect assumption of v8 coverage or is there an issue here?

Please provide a link to a minimal reproduction of the bug

No response

Please provide the exception or error you saw


Please provide the environment you discovered this bug in (run ng version)

Angular CLI       : 21.2.3
Angular           : 21.2.5
Node.js           : 22.13.1
Package Manager   : npm 10.9.2
Operating System  : darwin arm64

┌───────────────────────────────────┬───────────────────┬───────────────────┐
│ Package                           │ Installed Version │ Requested Version │
├───────────────────────────────────┼───────────────────┼───────────────────┤
│ @angular/animations               │ 21.2.5            │ ~21.2.0           │
│ @angular/build                    │ 21.2.3            │ ^21.2.0           │
│ @angular/cdk                      │ 21.2.3            │ ~21.2.0           │
│ @angular/cli                      │ 21.2.3            │ ~21.2.0           │
│ @angular/common                   │ 21.2.5            │ ~21.2.0           │
│ @angular/compiler                 │ 21.2.5            │ ~21.2.0           │
│ @angular/compiler-cli             │ 21.2.5            │ ~21.2.0           │
│ @angular/core                     │ 21.2.5            │ ~21.2.0           │
│ @angular/forms                    │ 21.2.5            │ ~21.2.0           │
│ @angular/language-service         │ 21.2.5            │ ~21.2.0           │
│ @angular/platform-browser         │ 21.2.5            │ ~21.2.0           │
│ @angular/platform-browser-dynamic │ 21.2.5            │ ~21.2.0           │
│ @angular/router                   │ 21.2.5            │ ~21.2.0           │
│ rxjs                              │ 7.8.2             │ ~7.8.2            │
│ typescript                        │ 5.9.3             │ ~5.9.3            │
│ vitest                            │ 4.1.0             │ ^4.0.18           │
│ zone.js                           │ 0.15.1            │ ~0.15.0           │
└───────────────────────────────────┴───────────────────┴──────────────────

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions