我的测试套件都通过了,但我抛出了以下错误:
'Error during cleanup of component' TypeError: Cannot read property 'unsubscribe' of undefined
它真的比任何其他东西都烦人,但我不知道如何删除它。
我的组件写如下:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { UserService } from 'src/app/services/user.service';
import { User } from 'src/models/user';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.scss']
})
export class UserListComponent implements OnInit, OnDestroy {
users: Array<User> = [];
private subscription: Subscription;
constructor(private service: UserService) { }
ngOnInit(): void {
this.subscription = this.service.getUserList().subscribe(data => this.users = data.list.entries)
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
remove(id: string): Array<User>{
return this.users = [...this.users].filter(user => user.entry.id !== id);
}
}
任何测试规范:
import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { UserListComponent } from './user-list.component';
import { UserService } from 'src/app/services/user.service';
import { HttpClientModule } from '@angular/common/http';
import { MatChipsModule } from '@angular/material/chips';
import { MatIconModule } from '@angular/material/icon';
import { DebugElement } from '@angular/core';
import { By } from "@angular/platform-browser";
import { of } from 'rxjs';
import { delay } from 'rxjs/operators';
describe('UserListComponent', () => {
let component: UserListComponent;
let fixture: ComponentFixture<UserListComponent>;
let userService: UserService;
let el: DebugElement;
let users: any = [{
"entry": {
"firstName": "Ryan",
"id": "ryan",
"enabled": false,
}
},
{
"entry": {
"firstName": "Administrator",
"id": "admin",
"enabled": true,
}
}
];
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [UserListComponent],
providers: [UserService],
imports: [HttpClientModule, MatChipsModule, MatIconModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(UserListComponent);
component = fixture.componentInstance;
userService = TestBed.get(UserService);
});
afterEach(() => {
fixture.destroy();
})
it('should create', () => {
fixture.detectChanges();
expect(component).toBeTruthy();
});
it('load users OnInit', fakeAsync(() => {
spyOn(userService, 'getUserList').and.returnValue(of({
list: {
entries: users
}
}).pipe(delay(1)));
fixture.detectChanges();
expect(component.users).toEqual([]);
expect(userService.getUserList).toHaveBeenCalled();
tick(1);
expect(component.users).toEqual(users);
}));
it('render the user list', fakeAsync(() => {
spyOn(userService, 'getUserList').and.returnValue(of({
list: {
entries: users
}
}).pipe(delay(1)));
fixture.detectChanges();
tick(1);
fixture.detectChanges();
el = fixture.debugElement.query(By.css('mat-chip-list'));
expect(el).toBeDefined();
expect(el.queryAll(By.css('mat-chip')).length).toEqual(2);
}));
it('should remove a user from the list', fakeAsync(() => {
spyOn(userService, 'getUserList').and.returnValue(of({
list: {
entries: users
}
}).pipe(delay(1)));
spyOn(component, 'remove').and.callThrough();
fixture.detectChanges();
tick(1);
fixture.detectChanges();
let removeIcons = fixture.debugElement.queryAll(By.css('mat-icon'));
expect(removeIcons.length).toEqual(1);
removeIcons[0].triggerEventHandler('click', {stopPropagation: function(){return false;}});
fixture.detectChanges();
expect(component.remove).toHaveBeenCalled();
expect(component.remove).toHaveBeenCalledWith('admin');
expect(component.users.length).toEqual(1);
let chips = fixture.debugElement.queryAll(By.css('mat-chip'));
expect(chips.length).toEqual(1);
}));
it('should differentiate an "enabled" user', () => {
component.users = users;
fixture.detectChanges();
let chips = fixture.nativeElement.querySelectorAll('mat-chip');
component.users.forEach((user, index) => {
expect(chips[index].classList.contains('mat-chip-with-trailing-icon')).toBe(user.entry.enabled ? true : false);
expect(window.getComputedStyle(fixture.nativeElement.querySelectorAll('mat-chip')[index]).backgroundColor).toBe(user.entry.enabled ? 'rgb(173, 255, 47)' : 'rgb(224, 224, 224)');
});
});
});
据我所知,该问题在ngOnDestroy
中,我们取消订阅可观察到的内容。我试着将this.subscription.unsubscribe();
包装在检查其定义中,但我不愿意更改应用程序代码以使测试通过
一些其他解决方案提到,在第一个断言should create
中添加fixture.detectChanges();
将触发ngOnInit
,但尽管测试现在通过了,错误仍然存在。
有什么想法吗?
这是因为您还没有订阅所有的测试用例,在这种情况下,订阅还没有形成。
因此,在取消订阅之前,最好检查订阅是否已形成。
ngOnDestroy(): void {
if(subscription) {
this.subscription.unsubscribe();
}
}
更新-仅在测试用例结束时需要更改
当您在ngOnInit内部订阅时,您需要确保在创建组件之前为其提供模拟数据。希望它能解决你的问题。
beforeEach(() => {
spyOn(userService, 'getUserList').and.returnValue(of({
list: {
entries: users
}
}).pipe(delay(1)));
fixture = TestBed.createComponent(UserListComponent);
component = fixture.componentInstance;
userService = TestBed.get(UserService);
});