我正在测试订阅路由器参数的组件。每个测试通过,一切正常。但如果我查看控制台,我会看到一个错误:
清理组件ApplicationViewComponent时出错 localConsole。(匿名函数)@ context.js:232
你知道为什么会这样吗?
我尝试从unsubscribe()
方法移除ngOnDestroy()
,错误消失。
karma / jasmine会自动支持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"
}
]);
}
}
答案 0 :(得分:64)
公认的解决方案并不是最优的,它可以解决测试未正确设置的问题。
组件清理期间&#34;错误&#34;发生错误消息,因为调用ngOnDestroy()
时,this.routeSubscription
未定义。发生这种情况是因为从未调用过ngOnInit()
,这意味着您从未订阅该路由。如Angular testing tutorial中所述,在您第一次致电fixture.detectChanges()
之前,该组件尚未完全初始化。
因此,正确的解决方案是在调用fixture.detectChanges()
后立即将beforeEach()
添加到createComponent
块。它可以在您创建夹具后随时添加。这样做将确保组件完全初始化,组件清理也将按预期运行。
答案 1 :(得分:28)
您需要重构您的方法ngOnDestroy,如下所示:
ngOnDestroy() {
if ( this.routeSubscription)
this.routeSubscription.unsubscribe();
}
答案 2 :(得分:5)
所以我的情况类似,但不完全相同:我只是把它放在这里以防其他人觉得它有用。当我用Jamine / Karma进行单元测试时
'ERROR: 'Error during cleanup of component','
事实证明,这是因为我没有正确处理我的观察者,他们也没有错误功能。所以修复是添加一个错误函数:
this.entityService.subscribe((items) => {
///Do work
},
error => {
this.errorEventBus.throw(error);
});
答案 3 :(得分:3)
添加@David Brown的回复,下面的代码对我有用。
.subscribe(res => {
...
},
error => Observable.throw(error)
)
答案 4 :(得分:2)
在类似的情况下,我想在组件本身上下文之外的组件中测试功能。
这对我有用:
afterEach(() => {
spyOn(component, 'ngOnDestroy').and.callFake(() => { });
fixture.destroy();
});
答案 5 :(得分:1)
就我而言,在每次测试解决问题后都销毁了组件。因此,您可以尝试将其添加到您的describe函数:
afterEach(() => {
fixture.destroy();
})
答案 6 :(得分:1)
您必须做两件事才能解决此错误。
1-在beforeEach()中添加 fixture.detectChanges();
2-您需要在下方添加内容,以便使组件清晰可见。
afterEach(() => {
fixture.destroy();
});
答案 7 :(得分:1)
如@randomPoison所述,当未初始化使用unsubscribe
的组件时,将触发错误。但是,对于相应组件的规范文件中的错误,调用fixture.detectChanges()
是一种解决方案。
但是我们可能还需要处理创建FooComponent
的{{1}},并且BarComponent
在其BarComponent
中使用unsubscribe
。必须进行适当的模拟。
我建议使用另一种方法来进行订阅清除,这是声明性的,不会触发此类问题。这是一个示例:
ngOnDestroy
有关此方法的更多信息here
答案 8 :(得分:0)
在我的情况下,错误在模板中。子组件ngDestroy中存在错误,因为我试图设置只读属性而没有被销毁。值得您花时间检查您的子组件是否被正确销毁。
答案 9 :(得分:0)
对我来说,解决此错误的原因是组件的ngOnDestroy内,我将商店的发货和退订包裹在try catch中。
ngOnDestroy(): void {
try {
this.store.dispatch(new foo.Bar(this.testThing()));
if(this.fooBarSubscription) {
this.fooBarSubscription.unsubscribe();
}
} catch (error) {
this.store.dispatch(new foo.Bar(this.testThing()));
}
}