GeezerVison is showing github.com — Inaccurate code coverage with aot testing · Issue #64583 · angular/angular
Inaccurate code coverage with aot testing #64583DescriptionCommand
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
Running ng test with AoT enabled produced inaccurate code coverage for branches, with signals (always?) marked as a not tested branch.
For example: input.required<UserModel>(); has <UserModel>() marked as untested (but input.required is marked as tested),
computed(...) or signal(...) have their last parenthesis marked as an untested branch as well.
Minimal Reproduction
This can be reproduced in a brand new CLI app.
npx @angular/[email protected] new aot-coverage --defaults --no-routing
cd aot-coverage
Running ng test --no-watch --code-coverage gives:
=============================== Coverage summary ===============================
Statements : 100% ( 5/5 )
Branches : 100% ( 0/0 )
Functions : 100% ( 0/0 )
Lines : 100% ( 3/3 )
================================================================================
Running ng test --no-watch --code-coverage --aot gives:
=============================== Coverage summary ===============================
Statements : 100% ( 50/50 )
Branches : 85.71% ( 6/7 )
Functions : 100% ( 7/7 )
Lines : 100% ( 43/43 )
================================================================================
In coverage/aot-coverage/app.ts.html we can see the last parenthesis of the signal is marked as not tested:
Exception or Error
Your Environment
Angular CLI : 21.0.0-next.8
Angular : 21.0.0-next.8
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-next.8 │ ^21.0.0-next.8 │
│ @angular/cli │ 21.0.0-next.8 │ ^21.0.0-next.8 │
│ @angular/common │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ @angular/compiler │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ @angular/compiler-cli │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ @angular/core │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ @angular/forms │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ @angular/platform-browser │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ @angular/router │ 21.0.0-next.8 │ ^21.0.0-next.0 │
│ rxjs │ 7.8.2 │ ~7.8.0 │
│ typescript │ 5.9.3 │ ~5.9.2 │
└───────────────────────────┴───────────────────┴───────────────────┘
Anything else relevant?
Note that the same happens with the unit-test runner, with Vitest or Karma. Reacted by Ken, rexsuecia, AndreasGartmyr, Loïc Chau and Jeroen Akkerman Activitychanged the title [-]Inaccurate code coverage ith aot testing[/-] [+]Inaccurate code coverage with aot testing[/+] on Oct 22, 2025clydin commented on Oct 22, 2025 Most likely cause is the signalMetadataTransform located here in the Angular compiler:
|
export function signalMetadataTransform(program: ts.Program): ts.TransformerFactory<ts.SourceFile> { |
Transferring to the framework repo for further triage. JeanMeche commented on Oct 23, 2025 I'm seeing that we apply the transform unconditionnaly. This raises 2 issues
- It applied even in prod mode, but DCE takes care of removing all the code we generate (this is sub optimal)
- We should probably only enable it for runtime only dev mode.
dgp1130 commented on Oct 27, 2025 I'm assuming the issue here is that we're effectively generating a conditional on ngDevMode along the lines of ngDevMode ? 'someDebugName' : undefined. Since testing only runs with ngDevMode === true, the false part of this branch is never executed, triggering coverage with weird line numbers, since this isn't present in source code.
We could disable the transform for ng test, but this is not trivial to do in google3, since we share a single emit format for both testing and non-testing use cases, so we can't easily determine when to enable / disable this transform.
I think the better solution is likely to continue to always use this transform but then include some kind of ignore comment telling the coverage tool to ignore the false conditional. c8 seems to support an ignore statement and I vaguely recall some kind of LCOV_IGNORE directive? Not sure of the full matrix of support coverage tools and their opt-outs to know how viable this really is. fagia commented on Dec 22, 2025 Still same issue also with Angular 21.0.1 Ritzlgrmft commented on Jan 12, 2026 still the same issue with Angular 21.0.5
is there any workaround beside ignoring signal lines like:
// c8 ignore next
protected readonly title = signal('my-signal');
Martinspire commented on Feb 26, 2026 So what can we do about this? Is there some setting that is conflicting or what? With Analog Vitest, I did not have this issue (perhaps because they compiled it themselves). But migration is blocked if the numbers aren't reported properly. I also think that ignoring code might lead to other issues as well. mpalourdio commented on Feb 26, 2026 I find this a little irritating too. We force quality gates in our CI (based on code coverage among other metrics), but for now, we had to disable this check for angular 21 projects.
Could this get a bit of love please ? Thanks! added a commit that references this issue on Feb 26, 2026This issue will close once commit 2e53627 is merged into the 'main' branch.2e53627 added a commit that references this issue on Mar 4, 2026This issue will close once commit 05d022d is merged into the 'main' branch.05d022d added a commit that references this issue on Mar 4, 2026This issue will close once commit dc4cf64 is merged into the 'main' branch.dc4cf64 added a commit that references this issue on Mar 24, 2026This issue will close once commit 292f0e9 is merged into the 'main' branch.292f0e9 angular-automatic-lock-bot commented on Apr 4, 2026 This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|