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

Angular服务Promise响应Jasmine测试失败,请求排查问题

问题排查:Angular Jasmine测试Promise响应失败

测试运行时出现错误:Expected 'yes' to be 'no',核心是测试Promise响应时的逻辑错误,以下是相关代码及修复方案:

相关代码

CustomerService

@Injectable({
  providedIn: "root"
})
export class CustomerService {
  constructor() {}

  isValid() : Promise<boolean> {
    return Promise.resolve(true);
  }
}

CustomerComponent

@Component({
  selector: 'app-customer',
  templateUrl: './customer.component.html',
  styleUrls: ['./customer.component.scss']
})
export class CustomerComponent implements OnInit {
  icon = new BehaviorSubject('yes');
  constructor(private readonly customerService : CustomerService) {}

  ngOnInit(): void {
    this.customerService.isValid().then(isValid => {
      this.icon.next(isValid ? 'yes' : 'no');
    });
  }
}

原测试代码(存在问题)

describe('CustomerComponent', () => {
  let component: CustomerComponent;
  let fixture: ComponentFixture<CustomerComponent>;
  let customerServiceSpy: jasmine.SpyObj<CustomerService>;

  beforeEach(async () => {
    customerServiceSpy = jasmine.createSpyObj<CustomerService>(['isValid']);

    await TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      declarations: [CustomerComponent],
      providers: [{ provide: CustomerService, useValue: customerServiceSpy }]
    }).compileComponents();

    fixture = TestBed.createComponent(CustomerComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

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

  fit('should change initial value of icon according to the response of isValid function', waitForAsync(() => {
    fixture.detectChanges();

    customerServiceSpy.isValid.and.returnValue(Promise.resolve(false));
    component.ngOnInit();

    fixture.whenStable().then(() => {
      fixture.detectChanges();
      expect(component.icon.value).toBe('no');
    });

    customerServiceSpy.isValid.and.returnValue(Promise.resolve(true));
    component.ngOnInit();

    fixture.whenStable().then(() => {
      fixture.detectChanges();
      expect(component.icon.value).toBe('yes');
    });
  }));
});

问题分析

  1. 异步逻辑顺序混乱:Promise是异步执行的,第一个whenStable()的回调还未执行时,就修改了spy的返回值并再次调用ngOnInit(),两个异步操作互相覆盖。第二个ngOnInit()会先将icon设为yes,而第一个测试的断言此时才执行,必然失败。
  2. 单测试用例混合多场景:一个测试用例应只验证一种场景(isValid返回true或false),同时测试两种情况违反了单一职责原则,容易引发逻辑冲突。

修复方案

将测试拆分为两个独立用例,分别验证isValid返回true和false的场景,确保每个用例中spy返回值设置正确,且等待异步操作完成后再执行断言。

修复后的测试代码

describe('CustomerComponent', () => {
  let component: CustomerComponent;
  let fixture: ComponentFixture<CustomerComponent>;
  let customerServiceSpy: jasmine.SpyObj<CustomerService>;

  beforeEach(async () => {
    customerServiceSpy = jasmine.createSpyObj<CustomerService>(['isValid']);

    await TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      declarations: [CustomerComponent],
      providers: [{ provide: CustomerService, useValue: customerServiceSpy }]
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(CustomerComponent);
    component = fixture.componentInstance;
    // 不提前调用fixture.detectChanges(),手动控制ngOnInit触发时机
  });

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

  it('should set icon to "no" when isValid returns false', waitForAsync(() => {
    customerServiceSpy.isValid.and.returnValue(Promise.resolve(false));
    component.ngOnInit();
    
    fixture.whenStable().then(() => {
      fixture.detectChanges();
      expect(component.icon.value).toBe('no');
    });
  }));

  it('should set icon to "yes" when isValid returns true', waitForAsync(() => {
    customerServiceSpy.isValid.and.returnValue(Promise.resolve(true));
    component.ngOnInit();
    
    fixture.whenStable().then(() => {
      fixture.detectChanges();
      expect(component.icon.value).toBe('yes');
    });
  }));
});

额外优化点

  • 拆分beforeEach:将模块配置与组件创建分离,避免重复配置模块,同时手动控制ngOnInit触发时机,提升测试灵活性。
  • 使用async/await简化异步代码(可选):让测试逻辑更易读,示例如下:
it('should set icon to "no" when isValid returns false', async () => {
  customerServiceSpy.isValid.and.returnValue(Promise.resolve(false));
  component.ngOnInit();
  
  await fixture.whenStable();
  fixture.detectChanges();
  expect(component.icon.value).toBe('no');
});

内容的提问来源于stack exchange,提问作者K.G.K.Thilina

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 09:15:23