私は、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');
});
これは、コンポーネントでChangeDetectionStrategy.OnPushを使用していることが原因であることがわかりました。 OnPushを使用すると、.detectChanges()を1回だけ呼び出すことができるため、その後の呼び出しでは何も実行できません。 Angular)について十分に理解していないため、その理由を完全に理解することはできません。
TestBed構成でChangeDetectionStrategyをオーバーライドすることにより、必要な動作を生成できました。
TestBed.configureTestingModule({
imports: [],
declarations: [TestComponent],
providers: []
})
.overrideComponent(TestComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
})
.compileComponents();
ここで提供される答え https://stackoverflow.com/a/50142134/3765819 は問題を修正します。ただし、UIの問題を回避できる別の方法もあります。私が抱えていた問題は、質問で説明した問題と似ていました。つまり、HTMLで特定の文字列をテストしたときに、それを見つけることができませんでした。コードを実行したときのコードは適切でしたが、UIはテスト時に更新されませんでした。
私がしなければならなかったことは:
ChangeDetectorRef
を.ts
ファイルに挿入するには:
constructor(private changeDetector: ChangeDetectorRef) {}
必要なときに呼び出します:
this.changeDetector.markForCheck();
私はこの質問が古いことを知っていますが、最近、同じ問題が発生しました。変更の検出は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;
});