詳細ガイド
テスト

コンポーネントテストの基本

コンポーネントは、Angularアプリケーションの他のすべての部分とは異なり、HTMLテンプレートとTypeScriptクラスを組み合わせたものです。 コンポーネントは、実際にはテンプレートとクラスが 連携 したものです。 コンポーネントを適切にテストするには、意図したとおりに連携して動作することをテストする必要があります。

このようなテストには、Angularと同様にブラウザのDOMにコンポーネントのホスト要素を作成し、そのテンプレートで記述されているように、DOMとのコンポーネントクラスの対話を調査することが必要です。

Angularの TestBed は、次のセクションで説明するように、この種類のテストを容易にします。 しかし、多くの場合、DOM を伴わないコンポーネントクラス単独のテスト は、コンポーネントの動作の大部分を、より簡単で明らかな方法で検証できます。

コンポーネント DOM テスト

コンポーネントは、そのクラスだけではありません。 コンポーネントはDOMと他のコンポーネントと対話します。 クラスだけでは、コンポーネントが正しくレンダリングされるか、ユーザーの入力やジェスチャーに応答するか、親コンポーネントと子コンポーネントと統合されるかを判断できません。

  • Lightswitch.clicked() はユーザーが呼び出せるように何かとバインドされていますか?
  • Lightswitch.message は表示されますか?
  • ユーザーは DashboardHeroComponent によって表示されるヒーローを実際に選択できますか?
  • ヒーローの名前は期待通り(たとえば、大文字)に表示されますか?
  • WelcomeComponent のテンプレートによってウェルカムメッセージは表示されますか?

これらの質問は、説明した前の簡単なコンポーネントにとっては問題ないかもしれません。 しかし、多くのコンポーネントは、そのテンプレートで記述されているDOM要素との複雑な対話を持ち、コンポーネントの状態が変わるとHTMLが表示および非表示になります。

これらの質問に答えるには、コンポーネントに関連付けられたDOM要素を作成し、DOMを調べて適切なタイミングでコンポーネントの状態が正しく表示されていることを確認します。そしてユーザーが画面と対話するようにシミュレートして、それらの対話がコンポーネントが期待通りに動作するかどうかを判断する必要があります。

これらの種類のテストを書くには、TestBed の追加機能と、他のテストヘルパーを使用します。

CLI で生成されたテスト

CLIは、新しいコンポーネントの生成を要求するときに、デフォルトで初期テストファイルを自動的に生成します。

たとえば、次のCLIコマンドは app/banner フォルダーに BannerComponent を生成します(インラインテンプレートとスタイル付き)。

      
ng generate component banner --inline-template --inline-style --module app

また、コンポーネントの初期テストファイル banner-external.component.spec.ts も生成し、次のようになります。

app/banner/banner-external.component.spec.ts (初期)

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

HELPFUL: compileComponents は非同期であるため、@angular/core/testing からインポートされた waitForAsync ユーティリティ関数を使用します。

詳細については、waitForAsync セクションを参照してください。

セットアップの削減

このファイルの最後の3行だけが実際にコンポーネントをテストしており、Angularがコンポーネントを作成できることをアサートするだけです。

ファイルの残りの部分は、コンポーネントが実質的なものへと進化した場合に必要になる可能性のある、より高度なテストを予期した定型文のセットアップコードです。

これらの高度なテスト機能については、次のセクションで説明します。 今のところ、このテストファイルをより管理しやすいサイズに大幅に縮小できます。

app/banner/banner-initial.component.spec.ts (最小限)

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

この例では、TestBed.configureTestingModule に渡されるメタデータオブジェクトは、単にテスト対象のコンポーネントである BannerComponent を宣言します。

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

HELPFUL: 他のものを宣言したりインポートする必要はありません。 デフォルトのテストモジュールは、@angular/platform-browserBrowserModule などのモジュールで事前に構成されています。

後で TestBed.configureTestingModule() を呼び出して、インポート、プロバイダー、その他の宣言を追加して、テストのニーズに合わせて構成します。 オプションの override メソッドは、構成の側面をさらに微調整できます。

createComponent()

TestBed を構成したら、その createComponent() メソッドを呼び出します。

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

TestBed.createComponent()BannerComponent のインスタンスを作成し、対応する要素をテストランナーのDOMに追加し、ComponentFixture を返します。

IMPORTANT: createComponent を呼び出した後に TestBed を再構成しないでください。

createComponent メソッドは、現在の TestBed 定義を凍結し、さらなる構成を締め切ります。

configureTestingModule()get()override... メソッドなど、TestBed の構成メソッドをさらに呼び出すことはできません。 呼び出そうとすると、TestBed はエラーをスローします。

ComponentFixture

ComponentFixture は、作成されたコンポーネントとその対応する要素を操作するためのテストハーネスです。

fixtureを介してコンポーネントインスタンスにアクセスし、Jasmineの期待を使用して存在を確認します。

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

beforeEach()

このコンポーネントが進化するにつれて、さらに多くのテストを追加するでしょう。 各テストのために TestBed 構成を複製するのではなく、セットアップをJasmineの beforeEach() といくつかのサポート変数にリファクタリングします。

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

次に、fixture.nativeElementからコンポーネントの要素を取得し、期待されるテキストを探すテストを追加します。

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

nativeElement

ComponentFixture.nativeElement の値は any 型です。 後で DebugElement.nativeElement に遭遇しますが、これも any 型です。

Angularは、コンパイル時に nativeElement がどのようなHTML要素であるか、あるいはHTML要素であるかどうかすらを知ることができません。 アプリケーションは、サーバーや Web Worker などの 非ブラウザープラットフォーム で実行されている可能性があり、その場合、要素のAPIが制限されているか、存在しない可能性があります。

このガイドのテストはブラウザで実行されるように設計されているため、nativeElement の値は常に HTMLElement またはその派生クラスのいずれかになります。

それが何らかの HTMLElement であることを知っているので、標準のHTML querySelector を使用して、要素ツリーをさらに深く掘り下げます。

次に、HTMLElement.querySelector を呼び出して段落要素を取得し、バナーテキストを探すテストを示します。

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

DebugElement

Angularの fixture は、fixture.nativeElement を介してコンポーネントの要素を直接提供します。

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

これは実際には、fixture.debugElement.nativeElement として実装された便利なメソッドです。

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

この回りくどい要素へのパスには、正当な理由があります。

nativeElement のプロパティは、実行時環境によって異なります。 これらのテストは、DOMがないか、DOMエミュレーションが完全な HTMLElement APIをサポートしていない 非ブラウザープラットフォーム で実行されている可能性があります。

Angularは、すべてのサポートされているプラットフォーム で安全に動作するために、DebugElement 抽象化に依存しています。 AngularはHTML要素ツリーを作成するのではなく、実行時プラットフォームの ネイティブ要素 をラップする DebugElement ツリーを作成します。 nativeElement プロパティは DebugElement をラップ解除し、プラットフォーム固有の要素オブジェクトを返します。

このガイドのサンプルテストはブラウザでのみ実行されるように設計されているため、これらのテストの nativeElement は常に HTMLElement であり、そのおなじみのメソッドとプロパティはテスト内で調べることができます。

次に、fixture.debugElement.nativeElement を使用して再実装された前のテストを示します。

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

DebugElement には、このガイドの他の場所で説明されているように、テストで役立つ他のメソッドとプロパティがあります。

Angularコアライブラリから DebugElement シンボルをインポートします。

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

By.css()

このガイドのテストはすべてブラウザで実行されますが、一部のアプリケーションは少なくとも一部の時間を別のプラットフォームで実行することがあります。

たとえば、コンポーネントは、接続の悪いデバイスでのアプリケーションの起動を高速化するための戦略の一部として、最初にサーバーでレンダリングされる可能性があります。 サーバー側レンダラーは、完全なHTML要素APIをサポートしていない可能性があります。 querySelector をサポートしていない場合、前のテストは失敗する可能性があります。

DebugElement は、すべてのサポートされているプラットフォームで動作するクエリメソッドを提供します。 これらのクエリメソッドは、DebugElement ツリー内のノードが選択基準に一致した場合に true を返す 述語 関数を取ります。

実行時プラットフォームのライブラリからインポートされた By クラスの助けを借りて 述語 を作成します。 次に、ブラウザプラットフォームの By のインポートを示します。

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

次の例は、DebugElement.query() とブラウザの By.css メソッドを使用して、前のテストを再実装したものです。

      
import {DebugElement} from '@angular/core';import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';import {By} from '@angular/platform-browser';import {BannerComponent} from './banner-initial.component';/*import { BannerComponent } from './banner.component';describe('BannerComponent', () => {*/describe('BannerComponent (initial CLI generated)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(waitForAsync(() => {    TestBed.configureTestingModule({imports: [BannerComponent]}).compileComponents();  }));  beforeEach(() => {    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;    fixture.detectChanges();  });  it('should create', () => {    expect(component).toBeDefined();  });});describe('BannerComponent (minimal)', () => {  it('should create', () => {    TestBed.configureTestingModule({imports: [BannerComponent]});    const fixture = TestBed.createComponent(BannerComponent);    const component = fixture.componentInstance;    expect(component).toBeDefined();  });});describe('BannerComponent (with beforeEach)', () => {  let component: BannerComponent;  let fixture: ComponentFixture<BannerComponent>;  beforeEach(() => {    TestBed.configureTestingModule({imports: [BannerComponent]});    fixture = TestBed.createComponent(BannerComponent);    component = fixture.componentInstance;  });  it('should create', () => {    expect(component).toBeDefined();  });  it('should contain "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    expect(bannerElement.textContent).toContain('banner works!');  });  it('should have <p> with "banner works!"', () => {    const bannerElement: HTMLElement = fixture.nativeElement;    const p = bannerElement.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.nativeElement)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const bannerEl: HTMLElement = bannerDe.nativeElement;    const p = bannerEl.querySelector('p')!;    expect(p.textContent).toEqual('banner works!');  });  it('should find the <p> with fixture.debugElement.query(By.css)', () => {    const bannerDe: DebugElement = fixture.debugElement;    const paragraphDe = bannerDe.query(By.css('p'));    const p: HTMLElement = paragraphDe.nativeElement;    expect(p.textContent).toEqual('banner works!');  });});

注目すべき観察結果をいくつか紹介します。

  • By.css() 静的メソッドは、標準の CSS セレクター を持つ DebugElement ノードを選択します。
  • クエリは、段落の DebugElement を返します。
  • その結果をラップ解除して、段落要素を取得する必要があります。

CSSセレクターでフィルタリングし、ブラウザの ネイティブ要素 のプロパティのみをテストする場合は、By.css アプローチはやり過ぎになる可能性があります。

多くの場合、querySelector()querySelectorAll() などの標準の HTMLElement メソッドを使用してフィルタリングする方が簡単で明確です。