You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 14+Jest测试中async管道找不到的问题求助

Angular测试中async管道找不到的解决办法

我使用Angular 14、Jest 28和Spectator进行组件测试,被测SidebarComponent的模板中使用async管道处理Observable类型的sidebarCollapseState$。运行测试时触发NG0302错误,提示在SidebarComponent中找不到async管道;移除该管道则错误消失,但业务需要保留它。测试文件未直接操作模板,以下是相关代码及配置:

模板代码

<aside class="sidebar" [style.width]="(sidebarCollapseState$ | async) ? '10rem' : '25.5rem'">
  <fmrs-side-menu
    [items]="menuItems"
  ></fmrs-side-menu>
  <p-divider></p-divider>
  <section class="sidebar__widget-container">
    <ng-content></ng-content>
  </section>
</aside>

报错信息

NG0302: The pipe 'async' could not be found in the 'SidebarComponent' component. Verify that it is declared or imported in this module.

      at getPipeDef (node_modules/@angular/core/fesm2020/core.mjs:22642:15)
      at ɵɵpipe (node_modules/@angular/core/fesm2020/core.mjs:22598:19)
      at SidebarComponent_Template (ng:/SidebarComponent.js:12:9)
      at executeTemplate (node_modules/@angular/core/fesm2020/core.mjs:12126:9)
      at renderView (node_modules/@angular/core/fesm2020/core.mjs:11948:13)
      at renderComponent (node_modules/@angular/core/fesm2020/core.mjs:13134:5)
      at renderChildComponents (node_modules/@angular/core/fesm2020/core.mjs:11807:9)
      at renderView (node_modules/@angular/core/fesm2020/core.mjs:11973:13)
      at ComponentFactory.create (node_modules/@angular/core/fesm2020/core.mjs:13913:13)
      at initComponent (node_modules/@angular/core/fesm2020/testing.mjs:26267:51)
      at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:409:30)
      at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:3830:43)
      at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:408:56)
      at Object.onInvoke (node_modules/@angular/core/fesm2020/core.mjs:26274:33)
      at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:408:56)
      at Zone.Object.<anonymous>.Zone.run (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:169:47)
      at NgZone.run (node_modules/@angular/core/fesm2020/core.mjs:26128:28)
      at TestBedImpl.createComponent (node_modules/@angular/core/fesm2020/testing.mjs:26270:41)
      at Function.createComponent (node_modules/@angular/core/fesm2020/testing.mjs:26078:37)
      at createSpectator (node_modules/@ngneat/spectator/fesm2020/ngneat-spectator.mjs:1533:29)
      at node_modules/@ngneat/spectator/fesm2020/ngneat-spectator.mjs:1525:27
      at src/app/layout/components/sidebar/sidebar.component.spec.ts:36:17
      at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:409:30)
      at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:3830:43)
      at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:408:56)
      at Zone.Object.<anonymous>.Zone.run (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:169:47)
      at Object.wrappedFunc (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:4330:34)

测试代码(sidebar.component.spec.ts)

describe('SidebarComponent', () => {
  let spectator: Spectator<SidebarComponent>

  const createComponent = createComponentFactory({
    component: SidebarComponent,
    declareComponent: false,
    imports: [
      HttpClientTestingModule,
      FormsModule,
      ReactiveFormsModule,
      RouterTestingModule
    ],
    providers: [
      mockProvider(ReactiveFormsModule),
      mockProvider(AuthService),
      mockProvider(EnvironmentConfigurationService),      
      provideMockStore({ initialState })
    ],
    schemas: [NO_ERRORS_SCHEMA]
  });

  beforeEach(() => {
    spectator = createComponent();
  });

  it('should create', () => {
    expect(spectator.component).toBeTruthy();
  });
});

Jest配置(jest.config.js)

module.exports = {
  preset: "jest-preset-angular",
  globalSetup: "jest-preset-angular/global-setup",
  moduleNameMapper: {
    "@shared/(.*)": "<rootDir>/src/app/shared/$1",
    "@core/(.*)": "<rootDir>/src/app/core/$1",
    '^src/(.*)': '<rootDir>/src/$1',
  },
  setupFilesAfterEnv: ["<rootDir>/src/setup-jest.ts"],
  testMatch: ["**/+(*.)+(spec).+(ts|js)?(x)"],
  transformIgnorePatterns: [
    "<rootDir>/node_modules/(?!.*.mjs$)"
  ],
  transform: {
    "^.+\.(ts|js|html)$": "jest-preset-angular",
  },
  collectCoverage: true,
  collectCoverageFrom: [
    "<rootDir>/src/app/**/*.component.ts",
    "!<rootDir>/src/app/**/*.component.html",
    "<rootDir>/src/app/**/*.service.ts",
    "!<rootDir>/src/app/**/*.pipe.ts",
    "!<rootDir>/src/app/**/*.directive.ts",
    "!<rootDir>/src/app/**/*.module.ts",
    "!<rootDir>/src/app/core/**",
    "!<rootDir>/dist/**",
    "!<rootDir>/src/app/shared/**",
    "!<rootDir>/src/app/auth/**"
  ],
  coverageReporters: ["html", "cobertura", "lcov", "text"],
  reporters: ["jest-junit", "default"],
  testPathIgnorePatterns: ["<rootDir>/node_modules/", "<rootDir>/dist/"],
  testRunner: "jest-jasmine2",
};

解决办法

问题原因

async管道属于Angular的CommonModule,实际应用中根模块或共享模块通常会导入这个模块,所以组件能正常使用。但测试环境是完全孤立的,Spectator基于TestBed构建测试环境,如果没有显式导入CommonModule,测试时就找不到async管道。

另外,测试代码里的mockProvider(ReactiveFormsModule)是错误用法——模块不能用mockProvider,应该放在imports数组里,这个配置可以直接删除。

修改后的测试代码

import { CommonModule } from '@angular/common';
// ...其他导入

describe('SidebarComponent', () => {
  let spectator: Spectator<SidebarComponent>

  const createComponent = createComponentFactory({
    component: SidebarComponent,
    declareComponent: false,
    imports: [
      CommonModule, // 新增:导入CommonModule
      HttpClientTestingModule,
      FormsModule,
      ReactiveFormsModule,
      RouterTestingModule
    ],
    providers: [
      // 移除错误的mockProvider(ReactiveFormsModule)
      mockProvider(AuthService),
      mockProvider(EnvironmentConfigurationService),      
      provideMockStore({ initialState })
    ],
    schemas: [NO_ERRORS_SCHEMA]
  });

  beforeEach(() => {
    spectator = createComponent();
  });

  it('should create', () => {
    expect(spectator.component).toBeTruthy();
  });
});

额外说明

如果SidebarComponent所在的业务模块已经导入了CommonModule,测试时也可以直接导入该业务模块,替代单独导入CommonModule。保持测试环境的依赖与实际组件运行环境一致,就能避免这类找不到内置管道/指令的问题。


内容的提问来源于stack exchange,提问作者David Khvedelidze

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 08:36:19