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

Angular独立指令单元测试中输入绑定失败问题排查

问题:Angular Signal Inputs在Jest单元测试中绑定失败

在为Angular CDK Table的自定义指令编写单元测试时,调用fixture.detectChanges()后抛出两个错误:

  1. Can't bind to 'sortableColumns' since it isn't a known property of 'table' (used in the 'TestComponent' component template)
  2. ERROR RuntimeError: NG0950: Input is required but no value is available yet.

测试组件代码

@Component({
  standalone: true,
  imports: [CdkTableModule, SortableTableDirective],
  template: ` <table cdk-table sortableTable [dataSource]="dataSource" [sortableColumns]="sortableColumns">
    <ng-container cdkColumnDef="columnA">
      <th cdk-header-cell *cdkHeaderCellDef>The First Column</th>
      <td cdk-cell *cdkCellDef="let element">{{ element.columnA }}</td>
    </ng-container>
    <ng-container cdkColumnDef="columnB">
      <th cdk-header-cell *cdkHeaderCellDef>The Second Column</th>
      <td cdk-cell *cdkCellDef="let element">{{ element.columnB }}</td>
    </ng-container>
    <ng-container cdkColumnDef="columnC">
      <th cdk-header-cell *cdkHeaderCellDef>The Third Column</th>
      <td cdk-cell *cdkCellDef="let element">{{ element.columnC }}</td>
    </ng-container>
    <tr cdk-header-row *cdkHeaderRowDef="displayedColumns"></tr>
    <tr cdk-row *cdkRowDef="let row; columns: displayedColumns"></tr>
  </table>`,
})
class TestComponent {
  dataSource = // 数据源创建逻辑已简化
  sortableColumns = ['columnA', 'columnB'];
  displayedColumns = ['columnA', 'columnB', 'columnC'];
}

测试初始化代码

beforeEach(async () => {
  await TestBed.configureTestingModule({
    imports: [SortableTableDirective, TestComponent, CdkTableModule],
  }).compileComponents();
  fixture = TestBed.createComponent(TestComponent);

  fixture.detectChanges();

  table = fixture.debugElement.query(By.directive(SortableTableDirective));
});

自定义指令代码

@Directive({
  selector: 'table[sortableTable]',
  standalone: true,
})
export class SortableTableDirective<T> implements OnDestroy {
  dataSource = input.required<SortableDatasource<T>>();
  sortableColumns = input.required<string[]>();

  public constructor(
    private renderer: Renderer2,
    private elRef: ElementRef,
    private containerRef: ViewContainerRef,
  ) {
    // 逻辑已简化
  }
}

补充信息

  • 生产环境代码运行正常,CDK Table元素可在fixture中正常获取;
  • 将signal inputs替换为传统@Input()装饰器后,绑定问题消失;
  • 最小复现项目在Karma+Jasmine环境下无此问题,仅Jest环境出现错误。

可能的原因与解决方案

原因

问题根源在于Jest测试工具链对Angular Signal Inputs的兼容性不足,尤其是较旧版本的@angular-builders/jest或jest-preset-angular,未能正确识别signal inputs对应的绑定属性。

解决方案

  • 升级测试依赖:更新@angular-builders/jest、jest-preset-angular到最新版本,新版本通常会修复signal inputs的测试支持问题。
  • 调整detectChanges调用时机:确保组件属性初始化完成后再调用fixture.detectChanges(),避免signal inputs在未获取值时被检测:
    beforeEach(async () => {
      await TestBed.configureTestingModule({
        imports: [SortableTableDirective, TestComponent, CdkTableModule],
      }).compileComponents();
      fixture = TestBed.createComponent(TestComponent);
      // 确保数据源等属性初始化完成
      fixture.componentInstance.dataSource = /* 完整的数据源实例 */;
      fixture.detectChanges();
      table = fixture.debugElement.query(By.directive(SortableTableDirective));
    });
    
  • 临时回退到@Input():如果升级依赖无法解决,可暂时将signal inputs替换为带required: true的@Input(),待Jest工具链完善后再切换回signal inputs:
    @Directive({
      selector: 'table[sortableTable]',
      standalone: true,
    })
    export class SortableTableDirective<T> implements OnDestroy {
      @Input({ required: true }) dataSource!: SortableDatasource<T>;
      @Input({ required: true }) sortableColumns!: string[];
    
      // 其余逻辑保持不变
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 05:04:59