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

如何对BehaviourSubject的.value属性进行单元测试?Angular报错解决

Angular单元测试报错:TypeError: Cannot read properties of undefined (reading 'value')

我是Angular新手,在写单元测试时遇到错误TypeError: Cannot read properties of undefined (reading 'value')。我的业务逻辑是:判断企业用户是否持有驾照,持有则把visible设为true,否则设为false。试过mock服务、返回假数据,但测试还是跑不通,代码如下:

hasLicense() {
  this.licenseService.getLicences(
    this.companyService.selectedCompany.value.companyId, 
    this.companyService.selectedCompany.value.userId
  ).subscribe(l => {
    if(l.driver) {
      this.visible = true;
    } else {
      this.visible = false;
    }
  });
} 

问题根源

报错核心是this.companyService.selectedCompany为undefined,直接访问其value属性导致空值异常。单元测试环境中未正确初始化该对象,导致方法执行时触发错误。

修复方案

1. 初始化CompanyService的selectedCompany

无论使用TestBed还是jest.mock,必须确保selectedCompany是一个包含value属性的对象(如果它是BehaviorSubject类型,需模拟为Observable)。

普通对象模拟(非Observable场景)

// 测试用例的beforeEach块中
const mockCompanyService = {
  selectedCompany: {
    value: { companyId: 'test-comp-id', userId: 'test-user-id' }
  }
};

TestBed.configureTestingModule({
  providers: [
    { provide: CompanyService, useValue: mockCompanyService }
  ]
});

BehaviorSubject模拟(Observable场景)

import { BehaviorSubject } from 'rxjs';

// 测试用例的beforeEach块中
const mockSelectedCompany = new BehaviorSubject({ 
  companyId: 'test-comp-id', 
  userId: 'test-user-id' 
});
const mockCompanyService = {
  selectedCompany: mockSelectedCompany
};

TestBed.configureTestingModule({
  providers: [
    { provide: CompanyService, useValue: mockCompanyService }
  ]
});

2. Mock LicenseService的getLicences方法

确保该方法返回可订阅的Observable,分别模拟“有驾照”和“无驾照”两种场景:

import { of } from 'rxjs';

// 测试用例的beforeEach块中
const mockLicenseService = jasmine.createSpyObj('LicenseService', ['getLicences']);

// 按需切换模拟返回值
mockLicenseService.getLicences.and.returnValue(of({ driver: true })); // 有驾照
// mockLicenseService.getLicences.and.returnValue(of({ driver: false })); // 无驾照

TestBed.configureTestingModule({
  providers: [
    { provide: LicenseService, useValue: mockLicenseService }
  ]
});

3. 编写完整测试用例

调用hasLicense()后,验证visible值是否符合预期,同时校验getLicences的调用参数:

it('当用户持有驾照时,visible应设为true', () => {
  const component = TestBed.inject(YourComponent);
  const licenseService = TestBed.inject(LicenseService) as jasmine.SpyObj<LicenseService>;
  const companyService = TestBed.inject(CompanyService);
  
  licenseService.getLicences.and.returnValue(of({ driver: true }));
  
  component.hasLicense();
  
  expect(component.visible).toBe(true);
  expect(licenseService.getLicences).toHaveBeenCalledWith(
    companyService.selectedCompany.value.companyId,
    companyService.selectedCompany.value.userId
  );
});

it('当用户无驾照时,visible应设为false', () => {
  const component = TestBed.inject(YourComponent);
  const licenseService = TestBed.inject(LicenseService) as jasmine.SpyObj<LicenseService>;
  
  licenseService.getLicences.and.returnValue(of({ driver: false }));
  
  component.hasLicense();
  
  expect(component.visible).toBe(false);
});

4. 代码健壮性优化(可选)

在业务代码中添加空值保护,避免运行时也出现相同错误:

hasLicense() {
  const selectedCompany = this.companyService.selectedCompany?.value;
  if (!selectedCompany) {
    this.visible = false;
    return;
  }
  this.licenseService.getLicences(selectedCompany.companyId, selectedCompany.userId)
    .subscribe(l => {
      this.visible = !!l.driver;
    });
}

内容的提问来源于stack exchange,提问作者Zepler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 15:50:23