Angular单元测试报错:Cannot read properties of undefined (reading 'subscribe')
解决Angular单元测试中“Cannot read properties of undefined (reading 'subscribe')”错误
问题原因
你用jasmine.createSpyObj创建的ApiService spy对象里,getRepo方法默认返回undefined,但组件的getUserRepos方法会直接调用该方法的subscribe方法,导致抛出“Cannot read properties of undefined (reading 'subscribe')”错误。真实的getRepo方法返回的是Angular的Observable,所以测试时必须让spy返回一个有效的Observable。
另外原测试代码的fixture.detectChanges()调用时机有误——该方法会触发组件的ngOnInit生命周期钩子,而此时getRepo的spy还没设置返回值,直接就会触发错误。
解决方案
- 导入RxJS的
Observable和对应操作符(比如of、throwError)来模拟API返回的数据流 - 在调用
fixture.detectChanges()之前,为getRepospy设置返回值,确保它返回一个Observable - 根据测试场景,可模拟成功响应、错误响应或空数据流
修改后的单元测试代码
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ApiService } from 'src/app/services/api.service'; import { UserProfileComponent } from './user-profile.component'; // 导入RxJS操作符用于模拟数据 import { of, throwError } from 'rxjs'; describe('UserProfileComponent', () => { let component: UserProfileComponent; let fixture: ComponentFixture<UserProfileComponent>; let apiService: jasmine.SpyObj<ApiService>; beforeEach(() => { apiService = jasmine.createSpyObj('ApiService', ['getUser', 'getRepo']); TestBed.configureTestingModule({ declarations: [UserProfileComponent], providers: [{ provide: ApiService, useValue: apiService }], }); fixture = TestBed.createComponent(UserProfileComponent); component = fixture.componentInstance; // 初始化测试数据 component.userDetail = { public_repos: 20 }; component.userName = 'testuser'; // 为getRepo spy设置成功响应的返回值 const mockRepos = [{ id: 1, name: 'repo1' }, { id: 2, name: 'repo2' }]; apiService.getRepo.and.returnValue(of(mockRepos)); // 现在再触发变更检测,执行ngOnInit fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); // 验证API调用后正确设置repoList it('should assign repoList after successful API call', () => { expect(apiService.getRepo).toHaveBeenCalledWith('testuser', 10, 1); expect(component.repoList.length).toBe(2); }); // 测试API错误处理流程 it('should handle API error properly', () => { const mockError = new Error('Request failed'); apiService.getRepo.and.returnValue(throwError(() => mockError)); component.getUserRepos(); expect(component.isLoading).toBe(true); // 若要验证setTimeout后的isLoading状态,需使用fakeAsync和tick // fakeAsync(() => { // tick(200); // expect(component.isLoading).toBe(false); // })(); }); });
组件代码小修正
组件错误处理逻辑里有个小问题:error.log(error)是无效调用,应该改为console.log(error),因为error对象本身没有log方法。
// 修正后的错误处理代码 (error) => { console.log(error); // 替换原有的error.log(error) setTimeout(() => { this.isLoading = false; }, 200); }
内容的提问来源于stack exchange,提问作者priyansh kasera
相关产品推荐
相关产品推荐

