単体テストで Angular OnInit ライフサイクルを再実行します


問題



問題は、コンポーネントの初期化ライフサイクル、具体的には単体テストを作成する際の OnInit ライフサイクルを再実行できるようにすることです.

ほとんどの場合、コンポーネントはいくつかの条件に基づいて異なる動作をします.

たとえば、ログイン ページまたはホームページを表示することを決定したいとします.そのため、ユーザーが既にログインしているかどうかを確認する必要があり、この確認は初期化中に行われる必要があります.

解決



コンポーネントをビルドするために Angular CLI を使用している場合、 ng generate component MyComponent を使用し、ボイラープレート ユニット テストを使用してコンポーネントを生成します.

単体テストファイルはこんな感じ

import {ComponentFixture, TestBed} from '@angular/core/testing';

import {MyComponent} from './my.component';

describe('MyComponent', () => {
    let component: MyComponent;
    let fixture: ComponentFixture<MyComponent>;

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            declarations: [
                MyComponent
            ]
        }).compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.createComponent(MyComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

    it('should create', () => {
        expect(component).toBeTruthy();
    });
});



ここで、fixture.detectChanges() は、コンポーネントで OnInit をトリガーするメソッドです.

これがわかったので、すでに解決策があります.

コンポーネントに @input() があるとします.これは、ユーザーの承認ステータスを示します.

@Component({
    selector: 'app-my-component',
    template: `
        <ng-container *ngIf="isLoggedIn else notLoggedInTemplate">
            <app-home></app-home>
        </ng-container>
        <ng-template #notLoggedInTemplate>
            <app-authorization></app-authorization>
        </ng-template>
    `
})
export class MyComponent implements OnInit {
    @Input() isLoggedIn: boolean;

    ngOnInit(): void {
        if (this.isLoggedIn)
            this.doSomethingBaseOnLogIn();
    }

}


したがって、このコンポーネント スクリプトに基づいて、コンポーネントを 2 回 (単体テスト内で) 作成し、isLoggedIn フィールドに渡し、fixture.detectChanges() を実行して、コンポーネントが期待どおりに動作するかどうかをテストできるようにする必要があります.

単体テストは次のようになります

describe('MyComponent', () => {
    let component: MyComponent;
    let fixture: ComponentFixture<MyComponent>;

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            declarations: [
                MyComponent,
                AuthorizationComponent,
                HomeComponent
            ]
        }).compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.createComponent(MyComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

    it('should create', () => {
        expect(component).toBeTruthy();
    });

    describe('Behave correctly base on "isLoggedIn" status', () => {
        it('should display login component if not logged in', () => {
            fixture = TestBed.createComponent(SearchFormComponent);
            component = fixture.componentInstance;
            component.isLoggedIn = false;
            fixture.detectChanges();

            const myComponent = fixture.debugElement.nativeElement as HTMLElement;
            expect(myComponent.querySelector('app-authorization')).toBeTruthy();
        });

        it('should display home component if already logged in', () => {
            fixture = TestBed.createComponent(SearchFormComponent);
            component = fixture.componentInstance;
            component.isLoggedIn = true;
            fixture.detectChanges();

            const myComponent = fixture.debugElement.nativeElement as HTMLElement;
            expect(myComponent.querySelector('app-home')).toBeTruthy();
        });
    });
});


これで、コンポーネントが初期化されるたびに条件を変更することで、渡された input に基づいてコンポーネントが期待どおりに動作することを確認できます.

コメントで質問があればお気軽にお尋ねください.