[英]Angular component test error: TypeError Cannot read property 'subscribe' of undefined
[英]Angular test not failing but throwing 'Error during cleanup of component' TypeError: Cannot read property 'unsubscribe' of undefined
我的測試套件都通過了,但是我拋出了以下錯誤:
'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)');
});
});
});
我知道問題出在我們取消訂閱 observable 的ngOnDestroy
中。 我嘗試包裝this.subscription.unsubscribe();
檢查其定義,但我不樂意更改應用程序代碼以使測試通過。
其他一些解決方案提到添加fixture.detectChanges();
在第一個斷言中should create
會觸發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);
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.