GeezerVison is showing github.com — NG 21 + Vitest + Non-stadanlone component with async pipe · Issue #65665 · angular/angular
NG 21 + Vitest + Non-stadanlone component with async pipe #65665DescriptionWhich @angular/* package(s) are the source of the bug?
common
Is this a regression?
Yes
Description
With Angular 21, I can't test (with vitest) a non-standalone component using the Async pipe. I get the following error:
X [ERROR] NG8004: No pipe found with name 'async'.
To fix this, import the "AsyncPipe" class from "@angular/common" and add it to the "imports" array of the module declaring the component. [plugin angular-compiler]
src/app/async-test/async-test.html:1:9:
1 │ <p>{{v | async }}</p>
╵ ~~~~~
Error occurs in the template of component AsyncTestComponent.
src/app/async-test/async-test.ts:6:15:
6 │ templateUrl: './async-test.html',
╵ ~~~~~~~~~~~~~~~~~~~
Here is the full code:
Test
describe('AsyncTestComponent', () => {
let component: AsyncTestComponent;
let fixture: ComponentFixture<AsyncTestComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CommonModule, AsyncPipe ],
declarations: [AsyncTestComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AsyncTestComponent);
component = fixture.componentInstance;
await fixture.whenStable();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
Component:
@Component({
selector: 'app-async-test',
templateUrl: './async-test.html',
styleUrl: './async-test.css',
standalone: false
})
export class AsyncTestComponent {
protected readonly v = of(1);
}
HTML:
Please provide a link to a minimal reproduction of the bug
ng new --> add a non standalone component and use the async pipe in it then run ng test
Please provide the exception or error you saw
I should be able to test my non-stadanlone component (using AsyncPipe) in angular 21.
Please provide the environment you discovered this bug in (run ng version)
Angular CLI : 21.0.0
Angular : 21.0.0
Node.js : 22.16.0
Package Manager : npm 10.9.2
Operating System : win32 x64
┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/build │ 21.0.0 │ ^21.0.0 │
│ @angular/cli │ 21.0.0 │ ^21.0.0 │
│ @angular/common │ 21.0.0 │ ^21.0.0 │
│ @angular/compiler │ 21.0.0 │ ^21.0.0 │
│ @angular/compiler-cli │ 21.0.0 │ ^21.0.0 │
│ @angular/core │ 21.0.0 │ ^21.0.0 │
│ @angular/forms │ 21.0.0 │ ^21.0.0 │
│ @angular/platform-browser │ 21.0.0 │ ^21.0.0 │
│ @angular/router │ 21.0.0 │ ^21.0.0 │
│ rxjs │ 7.8.2 │ ~7.8.0 │
│ typescript │ 5.9.3 │ ~5.9.2 │
│ vitest │ 4.0.13 │ ^4.0.8 │
└───────────────────────────┴───────────────────┴───────────────────
Anything else?
No response Reacted by ZeroTermi, paq1, Davz83, Geoffrey Crofte, Matthieu D., Charles-ris, F3YoD, AjayGhales, Christoph Meier, Rokas Brazdžionis and 7 more ActivityJoostK commented on Nov 26, 2025 This seems to be working as expected, although an exact reproduction is missing so I do not have the full picture here.
To run tests, the application is built like it normally would. As such, the AsyncTestComponent component is AOT compiled and the compiler rightfully concludes that the async pipe usage is broken, since no corresponding NgModule would be available that imports AsyncPipe or CommonModule. Scipionh commented on Nov 26, 2025 Here is a project where the problem occurs:
https://stackblitz.com/edit/hefgnomh
If I run
ng serve (or ng build) there is no issue, and the application has the desired behavior.
however if I run
ng test
✘ [ERROR] NG8004: No pipe found with name 'async'.
To fix this, import the "AsyncPipe" class from "@angular/common" and add it to the "imports" array of the module declaring the component. [plugin angular-compiler]
src/app/app.html:1:24:
1 │ Hello world: {{title | async}}
╵ ~~~~~
Error occurs in the template of component App.
src/app/app.ts:6:15:
6 │ templateUrl: './app.html',
╵ ~~~~~~~~~~~~
How can I make my test pass here ? Reacted by Charles-ris, F3YoD and hlovdal JoostK commented on Nov 26, 2025 That repro reveals a classic pitfall of NgModules. In a non-test build the main.ts file is used as entry-point which imports app.module.ts, making the compiler aware of AppModule and its contribution to AppComponent. In the test build, however, neither main.ts nor app.module.ts is part of the compilation, as it's not imported from anywhere nor is it present in tsconfig.spec.ts#include. Scipionh commented on Nov 26, 2025 I see, however I was under the assumption that the module created by my TestBed.configureTestingModule would be self sufficient.
What would you say is the best way to fix things here ? Include the modules in my tsconfig.spec.ts ? JoostK commented on Nov 26, 2025
I was under the assumption that the module created by my TestBed.configureTestingModule would be self sufficient.
That's for JIT (re)compiling the component, it is not taken into account during the AOT compilation.
What would you say is the best way to fix things here ? Include the modules in my tsconfig.spec.ts ?
Switch to standalone components :-) But yeah, having the same file in the compilation unit by using tsconfig.spec.json#includes would be the next best alternative. Scipionh commented on Nov 27, 2025 Yeah, migrating to standalone is the plan.
Unless, I missed something obvious, I went again through the whole testing documentation, and I really feel a caveat should be added to offer light on this particular issue. Adding modules to the tsconfig.spec.json feels completely counter-intuitive. alan-agius4 commented on Nov 27, 2025 To resolve this issue you could update the tsconfig as below:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": ["vitest/globals"]
},
"include": ["src/**/*.ts"]
}
Alternatively, you could use JIT instead of AOT for tests. Reacted by Serhii Serdiuk pshurygin commented on Feb 5, 2026 Can someone please explain how this can be considered "working as intended" if it was clearly a working and widely used scenario with v20+karma, but it no longer works in v21+vitest? I don't think NgModule or non-standalone components support is dropped or even deprecated in v21, so I would expect this to still work without the full migration to drop NgModules in favour of standalone.
I also didn't really understand this explanation:
That repro reveals a classic pitfall of NgModules. In a non-test build the main.ts file is used as entry-point which imports app.module.ts, making the compiler aware of AppModule and its contribution to AppComponent. In the test build, however, neither main.ts nor app.module.ts is part of the compilation, as it's not imported from anywhere nor is it present in tsconfig.spec.ts#include.
Why is the AppModule/AppComponent is even relevant in this discussion? As we see in the reproduction scenario, CommonModule(which exports async pipe) is explicitly imported into the TestingModule itself, so it's not clear why the compiler needs something else to make this pipe usable within this module. Reacted by Paul Silvestri, Arnaud Réthoré, Thomas and hlovdal JeanMeche commented on Feb 5, 2026 The main difference is that Karma use JIT by default and Vitest uses AOT by default.
Compilation assumptions are different. JoostK commented on Feb 5, 2026 As @JeanMeche mentions, Vitest vs Karma is not the main differentiator here, it's JIT vs AOT. When using Vitest with JIT compilation issues like the one reported won't occur, and vice versa when using AOT compiled builds for Karma you'd run into the same errors.
Why is the AppModule/AppComponent is even relevant in this discussion? As we see in the reproduction scenario, CommonModule(which exports async pipe) is explicitly imported into the TestingModule itself, so it's not clear why the compiler needs something else to make this pipe usable within this module.
TestBed is runtime-only infrastructure that the AOT-compiler doesn't take into account, and arguably can't take into account since TestBed redeclares component declarations. Since multiple TestBed interactions can redeclare components in different ways, there cannot be a single AOT compiled component definition. So, using the AOT compiler will compile the code as it normally would (i.e. for all @{Component,Directive,Pipe,NgModule,Injectable} decorated classes), which also means that non-standalone components require their NgModule to be present.
pshurygin commented on Feb 5, 2026 Thank you, this does make more sense now. I was under the impression that karma used AOT by default this whole time, as AOT has been the default compiler for the main application for 6 years now, but apparently that is not true. And so switching to v21 with Vitest also silently switches your tests from JIT to AOT, which was not obvious at all, and introduces all these module limitations.
So what are the intended preconditions to run tests in AOT mode? Would this ever only be possible if you drop the NgModules and use standalone for all of the tested components? hlovdal commented on Oct 26, 2026 Confirming that putting
into the configuration used by testing makes a lot of problems go away (Angular 21 project with very old roots).
$ npx ng config | jq '.. | objects | select(has("architect")) | .architect.build.configurations.development.aot'
false
$ npx ng config | jq '.. | objects | select(has("architect")) | .architect.test.options.buildTarget'
"myprojectt:build:development"
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|