Angular 2アプリに単体テストを追加しようとしています。私のコンポーネントの1つに、(click)
ハンドラーを持つボタンがあります。ユーザーがボタンをクリックすると、.ts
クラスファイルで定義されている関数が呼び出されます。その関数は、ボタンが押されたことを示すメッセージをconsole.logウィンドウに出力します。私の現在のテストコードは、console.log
メッセージの印刷をテストします。
describe('Component: ComponentToBeTested', () => {
var component: ComponentToBeTested;
beforeEach(() => {
component = new ComponentToBeTested();
spyOn(console, 'log');
});
it('should call onEditButtonClick() and print console.log', () => {
component.onEditButtonClick();
expect(console.log).toHaveBeenCalledWith('Edit button has been clicked!);
});
});
ただし、これはコントローラークラスのみをテストし、HTMLはテストしません。 onEditButtonClick
が呼び出されたときにロギングが発生することをテストするだけではありません。また、ユーザーがコンポーネントのHTMLファイルで定義されている編集ボタンをクリックしたときにonEditButtonClick
が呼び出されることをテストします。どうやってやるの?
私の目的は、ユーザーが編集ボタンをクリックしたときに 'onEditButtonClick'が呼び出されるかどうかを確認することであり、印刷されるconsole.logだけを確認することではありません。
最初にAngular TestBed
を使用してテストをセットアップする必要があります。この方法で、実際にボタンをつかんでクリックできます。あなたがすることは、ちょうどテスト環境のために、あなたが@NgModule
をするように、モジュールを設定することです
import { TestBed, async, ComponentFixture } from '@angular/core/testing';
describe('', () => {
let fixture: ComponentFixture<TestComponent>;
let component: TestComponent;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ ],
declarations: [ TestComponent ],
providers: [ ]
}).compileComponents().then(() => {
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
});
}));
});
次に、onEditButtonClick
メソッドをスパイし、ボタンをクリックして、メソッドが呼び出されたことを確認する必要があります
it('should', async(() => {
spyOn(component, 'onEditButtonClick');
let button = fixture.debugElement.nativeElement.querySelector('button');
button.click();
fixture.whenStable().then(() => {
expect(component.onEditButtonClick).toHaveBeenCalled();
});
}));
ここでは、ボタンクリックに非同期イベント処理が含まれているため、async
テストを実行する必要があり、fixture.whenStable()
を呼び出してイベントの処理を待機する必要があります。
関連項目:
ブラウザ内のイベントは非同期であり、イベントループ/キューにプッシュされるため、'@angular/core/testing'
が提供するasync
/fakeAsync
関数を使用してイベントをテストできます。
以下は、fakeAsync
を使用してクリックイベントをテストする非常に基本的な例です。
fakeAsync
関数は、特別なfakeAsync
テストゾーンでテスト本体を実行することにより、線形コーディングスタイルを有効にします。
ここでは、クリックイベントによって呼び出されるメソッドをテストしています。
it('should', fakeAsync( () => {
fixture.detectChanges();
spyOn(componentInstance, 'method name'); //method attached to the click.
let btn = fixture.debugElement.query(By.css('button'));
btn.triggerEventHandler('click', null);
tick(); // simulates the passage of time until all pending asynchronous activities finish
fixture.detectChanges();
expect(componentInstance.methodName).toHaveBeenCalled();
}));
以下は Angular docs が言うべきことです:
非同期に対するfakeAsyncの主な利点は、テストが同期しているように見えることです。目に見える制御の流れを妨げる
then(...)
はありません。約束を返すfixture.whenStable
はなくなり、tick()
に置き換えられましたそこにare制限があります。たとえば、
fakeAsync
内からXHR呼び出しを行うことはできません。
Angular 6を使用しています。私はMav55の答えに従いましたが、うまくいきました。ただし、fixture.detectChanges();
が本当に必要かどうかを確認したかったので、それを削除しても機能しました。次に、tick();
を削除して、機能するかどうかを確認しました。最後にfakeAsync()
ラップからテストを削除しましたが、驚いたことに動作しました。
だから私はこれで終わった:
it('should call onClick method', () => {
const onClickMock = spyOn(component, 'onClick');
fixture.debugElement.query(By.css('button')).triggerEventHandler('click', null);
expect(onClickMock).toHaveBeenCalled();
});
そして、それはうまくいきました。
同様の問題(詳細な説明)があり、元のtick
と同じ(またはより大きい)ミリ秒でsetTimeout
関数を使用して(jasmine-core: 2.52
で)解決しましたコール。
たとえば、setTimeout(() => {...}, 2500);
がある場合(2500ミリ秒後にトリガーされる)、tick(2500)
を呼び出し、で問題を解決します。
Deleteボタンのクリックに対する反応として、コンポーネントにあったもの:
delete() {
this.myService.delete(this.id)
.subscribe(
response => {
this.message = 'Successfully deleted! Redirecting...';
setTimeout(() => {
this.router.navigate(['/home']);
}, 2500); // I wait for 2.5 seconds before redirect
});
}
彼女は私の working テストです:
it('should delete the entity', fakeAsync(() => {
component.id = 1; // preparations..
component.getEntity(); // this one loads up the entity to my component
tick(); // make sure that everything that is async is resolved/completed
expect(myService.getMyThing).toHaveBeenCalledWith(1);
// more expects here..
fixture.detectChanges();
tick();
fixture.detectChanges();
const deleteButton = fixture.debugElement.query(By.css('.btn-danger')).nativeElement;
deleteButton.click(); // I've clicked the button, and now the delete function is called...
tick(2501); // timeout for redirect is 2500 ms :) <-- solution
expect(myService.delete).toHaveBeenCalledWith(1);
// more expects here..
}));
追伸fakeAsync
およびテストの一般的な非同期に関する優れた説明は、ここにあります: Angular 2-Julie Ralphによる8からのテスト戦略に関するビデオ:10、持続時間4分:)