web-dev-qa-db-ja.com

.detectChanges()がAngular test内で機能しない

私は、Angularで開発されたチャットアプリのテストの作成を任されています。以下は、現在テストを記述しているAngularテンプレートコードのスニペットです。

<div class="title-menu-container" fxLayoutAlign="center center">
  <button id="save-title-button" mat-icon-button *ngIf="titleInputEdit; else settings">
    <mat-icon class="secondary-text" (click)="saveTitle(titleInput.value)">check</mat-icon>
  </button>
  <ng-template #settings>
    <button mat-icon-button [matMenuTriggerFor]="menu" [disabled]="!(isGroupConversation$ | async)">
      <mat-icon class="secondary-text">settings</mat-icon>
    </button>
  </ng-template>
</div>

基本的に、コンポーネントのブール変数 'titleInputEdit'がtrueの場合、save-title-buttonが表示されます。それ以外の場合は、設定ボタンが表示されます。問題を引き起こしているテストケースは次のとおりです。

it('save title button should be present', () => {
  component.titleInputEdit = true;
  fixture.detectChanges();
  expect(fixture.nativeElement.querySelector('#save-title-button')).not.toBe(null);
}); 

私は単にコンポーネント変数を「モック」し、.detectChanges()を呼び出してから、ボタンの存在をテストします。ただし、テストは「期待されるnullはnullではない」で失敗します。

さまざまなconsole.log呼び出しを通じて、component.titleInputEditが正しくtrueに設定されていることを確認しましたが、fixture.nativeElementには正しいボタンが含まれていません。

私が気づいたいくつかのこと:

  • 'component.titleInputEdit = true'行をbeforeEachに移動して削除し、テストからdetectChanges()呼び出しを実行すると、テストに合格します。

    beforeEach(() => {
      fixture = TestBed.createComponent(TestComponent);
      component = fixture.componentInstance;
      component.titleInputEdit = true
      fixture.detectChanges();
      debugElement = fixture.debugElement;
    });     
    
    it('save title button should be present', () => {
        expect(fixture.nativeElement.querySelector('#save-title-button')).not.toBe(null);
    });
    
  • BeforeEach()から.detectChanges()呼び出しを削除し、テストケースに残した場合、テストは成功します。

私はAngularに比較的慣れていませんが、同様の問題を抱えている人々の事例を見つけました。これらの投稿で推奨されていることをいくつか試した後も、頭を悩ませています。さらに奇妙なのは、他のAngularコンポーネントについて、問題なくほぼ同じことを行うテストを書いたことです。

Angular docsで提供されている例も、非常によく似たものを示しています:

it('should display a different test title', () => {
  component.title = 'Test Title';
  fixture.detectChanges();
  expect(h1.textContent).toContain('Test Title');
});    
10
LHB

これは、コンポーネントでChangeDetectionStrategy.OnPushを使用していることが原因であることがわかりました。 OnPushを使用すると、.detectChanges()を1回だけ呼び出すことができるため、その後の呼び出しでは何も実行できません。 Angular)について十分に理解していないため、その理由を完全に理解することはできません。

TestBed構成でChangeDetectionStrategyをオーバーライドすることにより、必要な動作を生成できました。

TestBed.configureTestingModule({
    imports: [],
    declarations: [TestComponent],
    providers: []
  })
    .overrideComponent(TestComponent, {
      set: { changeDetection: ChangeDetectionStrategy.Default }
    })
    .compileComponents();
26
LHB

ここで提供される答え https://stackoverflow.com/a/50142134/3765819 は問題を修正します。ただし、UIの問題を回避できる別の方法もあります。私が抱えていた問題は、質問で説明した問題と似ていました。つまり、HTMLで特定の文字列をテストしたときに、それを見つけることができませんでした。コードを実行したときのコードは適切でしたが、UIはテスト時に更新されませんでした。

私がしなければならなかったことは:

ChangeDetectorRef.tsファイルに挿入するには:

constructor(private changeDetector: ChangeDetectorRef) {}

必要なときに呼び出します:

this.changeDetector.markForCheck();
1
fernandodof

私はこの質問が古いことを知っていますが、最近、同じ問題が発生しました。変更の検出は1回しか行われないため、スピナーがKarmaページで常に回転するというものです。私にとっての修正は、fixture.detectChanges(true)を呼び出すか、fixture.autoDetectChanges(true)を呼び出すかです。

beforeEach(() => { 
  fixture = TestBed.createComponent(TestComponent);
  component = fixture.componentInstance;
  component.titleInputEdit = true
  // 'detectChanges' will only test for onPush events: 
  // fixture.detectChanges();

  // 'autoDetectChanges' will continually check for changes until the test is complete.  
  // This is slower, but necessary for certain UI changes
  fixture.autoDetectChanges(true);

  debugElement = fixture.debugElement;
}); 
0
LethargicGeek