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