You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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还没设置返回值,直接就会触发错误。

解决方案

  1. 导入RxJS的Observable和对应操作符(比如of、throwError)来模拟API返回的数据流
  2. 在调用fixture.detectChanges()之前,为getRepo spy设置返回值,确保它返回一个Observable
  3. 根据测试场景,可模拟成功响应、错误响应或空数据流

修改后的单元测试代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 02:25:04