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

Angular 9中BehaviorSubject相关服务与组件单元测试编写及报错解决求助

Angular 9 单元测试解决方案

错误原因说明

currentUser是UserService的公开只读Observable属性,不是方法。jasmine.createSpyObj默认仅生成方法的间谍对象,如果你之前将currentUser作为方法名传入createSpyObj的第二个参数,就会出现「currentUser不是函数」的报错,正确做法是通过createSpyObj的第三个参数声明要模拟的属性。


1. UserService 单元测试代码(user.service.spec.ts)

import { TestBed } from '@angular/core/testing';
import { UserService } from './user.service';
import { User } from './user.model';

describe('UserService', () => {
  let service: UserService;

  beforeEach(() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(UserService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('setCurrentUser should update currentUser observable', (done) => {
    const testUser: User = { username: 'testUser', id: 1 };
    // 订阅currentUser验证返回值
    service.currentUser.subscribe(user => {
      if (user.username === testUser.username) {
        expect(user).toEqual(testUser);
        done();
      }
    });
    service.setCurrentUser(testUser);
  });
});

2. HelloComponent 单元测试代码(hello.component.spec.ts)

这里采用模拟UserService的方案,完全隔离外部依赖,符合单元测试规范:

import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { HelloComponent } from './hello.component';
import { UserService } from './user.service';
import { BehaviorSubject } from 'rxjs';
import { User } from './user.model';
import { By } from '@angular/platform-browser';

describe('HelloComponent', () => {
  let component: HelloComponent;
  let fixture: ComponentFixture<HelloComponent>;
  let mockUserSubject: BehaviorSubject<User>;
  let mockUserService: jasmine.SpyObj<UserService>;

  beforeEach(async () => {
    // 初始化模拟用户流
    mockUserSubject = new BehaviorSubject<User>({} as User);
    // 正确创建带属性的间谍对象:第二个参数是要模拟的方法,第三个参数是要模拟的属性
    mockUserService = jasmine.createSpyObj(
      'UserService',
      ['setCurrentUser'],
      { currentUser: mockUserSubject.asObservable() }
    );

    await TestBed.configureTestingModule({
      declarations: [ HelloComponent ],
      providers: [
        { provide: UserService, useValue: mockUserService }
      ]
    })
    .compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(HelloComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('初始状态无用户时,username为空,模板不显示问候语', () => {
    expect(component.username).toBeUndefined();
    const h1Element = fixture.debugElement.query(By.css('h1'));
    expect(h1Element).toBeFalsy();
  });

  it('服务推送新用户时,username更新,模板显示对应问候语', fakeAsync(() => {
    const testUser: User = { username: 'jack', id: 2 };
    // 推送新用户
    mockUserSubject.next(testUser);
    tick();
    fixture.detectChanges();
    // 验证组件属性
    expect(component.username).toBe('jack');
    // 验证模板渲染
    const h1Element = fixture.debugElement.query(By.css('h1'));
    expect(h1Element.nativeElement.textContent).toContain('Hello jack!');
  }));

  it('组件销毁时自动取消订阅', () => {
    // 给订阅的unsubscribe方法加间谍
    const unsubscribeSpy = spyOn(component['userServiceSubscription'] as any, 'unsubscribe');
    fixture.destroy();
    expect(unsubscribeSpy).toHaveBeenCalled();
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 21:45:06