購読解除を使用した Angular コンポーネントのテスト コンポーネントのクリーンアップ中にエラーが発生しました 質問する

購読解除を使用した Angular コンポーネントのテスト コンポーネントのクリーンアップ中にエラーが発生しました 質問する

ルーターパラメータをサブスクライブするコンポーネントをテストしています。すべてのテストに合格し、すべて正常に動作します。しかし、コンソールを見ると、エラーが表示されます。

コンポーネント ApplicationViewComponent localConsole.(匿名関数) のクリーンアップ中にエラーが発生しました @ context.js:232

なぜこのようなことが起こるのかご存知ですか?

unsubscribe()fromメソッドを削除してみたngOnDestroy()ところ、エラーは消えました。

カルマ/ジャスミンはunsubscribe()自動的にサポートされますか?

コンポーネントとテストはこちら

成分

import { Component, OnInit } from '@angular/core';   
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Rx'

import { AppService } from 'app.service';

@Component({
  selector: 'app-component',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  private routeSubscription: Subscription;

  // Main ID
  public applicationId: string;


  constructor(
    private route: ActivatedRoute,
    private _service: AppService
  ) { }

  ngOnInit() {
    this.routeSubscription = this.route.params.subscribe(params => {
      this.applicationId = params['id'];

      this.getDetails();
      this.getList();
    });
  }

  getDetails() {
    this._service.getDetails(this.applicationId).subscribe(
      result => {     
        console.log(result);
      },
      error => {  
        console.error(error);        
      },
      () => {
        console.info('complete');
      }
    );
  }

  getList(notifyWhenComplete = false) {
    this._service.getList(this.applicationId).subscribe(
      result => {     
        console.log(result);
      },
      error => {  
        console.error(error);        
      },
      () => {
        console.info('complete');
      }
    );
  }

  ngOnDestroy() {
    this.routeSubscription.unsubscribe();
  }

}

コンポーネント仕様ファイル

import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
  async,
  fakeAsync,
  ComponentFixture,
  TestBed,
  tick,
  inject
} from '@angular/core/testing';
import {
  RouterTestingModule
} from '@angular/router/testing';
import {
  HttpModule
} from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Router, ActivatedRoute } from '@angular/router';

// Components
import { AppComponent } from './app.component';

// Service
import { AppService } from 'app.service';
import { AppServiceStub } from './app.service.stub';

let comp:    AppComponent;
let fixture: ComponentFixture<AppComponent>;
let service: AppService;

let expectedApplicationId = 'abc123';

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [AppComponent],
      imports: [RouterTestingModule, HttpModule],
      providers: [
        FormBuilder,
        {
          provide: ActivatedRoute,
          useValue: {
            params:  Observable.of({id: expectedApplicationId})
          }
        },
        {
          provide: AppService,
          useClass: AppServiceStub
        }    
      ],
      schemas: [ NO_ERRORS_SCHEMA ]
    })
    .compileComponents();
  }));

  tests();
});

function tests() {
  beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    comp = fixture.componentInstance;

    service = TestBed.get(AppService);
  });


  /*
  *   COMPONENT BEFORE INIT
  */
  it(`should be initialized`, () => {
    expect(fixture).toBeDefined();
    expect(comp).toBeDefined();
  });


  /*
  *   COMPONENT INIT
  */

  it(`should retrieve param id from ActivatedRoute`, async(() => {
    fixture.detectChanges();

    expect(comp.applicationId).toEqual(expectedApplicationId);
  }));

  it(`should get the details after ngOnInit`, async(() => {
    spyOn(comp, 'getDetails');
    fixture.detectChanges();

    expect(comp.getDetails).toHaveBeenCalled();
  }));

  it(`should get the list after ngOnInit`, async(() => {
    spyOn(comp, 'getList');
    fixture.detectChanges();

    expect(comp.getList).toHaveBeenCalled();
  }));
}

サービス.スタブ

import { Observable } from 'rxjs/Observable';

export class AppServiceStub {
  getList(id: string) {
    return Observable.from([              
      {
        id: "7a0c6610-f59b-4cd7-b649-1ea3cf72347f",
        name: "item 1"
      },
      {
        id: "f0354c29-810e-43d8-8083-0712d1c412a3",
        name: "item 2"
      },
      {
        id: "2494f506-009a-4af8-8ca5-f6e6ba1824cb",
        name: "item 3"      
      }
    ]);
  }
  getDetails(id: string) {
    return Observable.from([      
      {        
        id: id,
        name: "detailed item 1"         
      }
    ]);
  }
}

ベストアンサー1

「コンポーネントのクリーンアップ中にエラーが発生しました」というエラーメッセージは、ngOnDestroy()が呼び出されたときにthis.routeSubscription未定義であるために発生します。これは、ngOnInit()が呼び出されなかったために発生します。つまり、ルートをサブスクライブしたことがないということです。Angular テストチュートリアルfixture.detectChanges()、最初に呼び出すまでコンポーネントは完全に初期化されません。

したがって、正しい解決策は、が呼び出された直後にブロックfixture.detectChanges()に追加することです。これは、フィクスチャを作成した後であればいつでも追加できます。そうすることで、コンポーネントが完全に初期化され、コンポーネントのクリーンアップも期待どおりに動作するようになります。beforeEach()createComponent

おすすめ記事