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

如何在Jasmine中测试Promise内的Angular HttpClient方法?

解决Promise内部HTTP方法测试失败的问题

看起来你在测试Angular异步验证器时遇到了两个关键问题:一个是方法名不匹配导致的直接报错,另一个是异步防抖逻辑的测试处理问题,我来帮你一步步拆解解决:

1. 修复核心报错:方法名不匹配

你遇到的Uncaught TypeError: _this._http.get$ is not a function,根源是测试代码里Spy监听的方法名和实际HttpService的方法名不一致:

  • 你的HttpService里暴露的方法是get$
  • 但测试代码里写的是spyOn(http, 'getAll$')

把Spy代码改成下面这样就能解决这个报错:

getSpy = spyOn(http, 'get$');

2. 处理防抖(Debounce)的异步超时问题

你的验证器里用了setTimeout做400ms防抖,测试时如果不模拟时间流逝,测试会在异步操作完成前就结束,导致断言失败。可以用Angular测试工具里的fakeAsync和tick来处理:

修改测试用例,比如第三个断言可以改成:

import { fakeAsync, tick } from '@angular/core/testing';

// ...

it('returns null if the http call cannot find a record', fakeAsync(() => {
  getSpy.and.returnValue(Observable.of({ data: null }));
  control = new FormControl('bobby');
  // 补上之前遗漏的authToken参数,避免潜在的参数问题
  result = service.recordExistsOnServer('user', 'username', 'bob', 'test-token');
  let validationResult;
  
  result(control).then(r => {
    validationResult = r;
  });

  tick(400); // 模拟400ms的防抖等待时间,触发HTTP请求
  expect(validationResult).toEqual(null);
  // 还可以验证HTTP请求是否被正确调用
  expect(getSpy).toHaveBeenCalledWith('/users', 'test-token', [{ key: 'username', value: 'bobby' }]);
}));

3. 补充其他测试细节

  • 补全遗漏的参数:你第二个测试用例里漏传了authToken参数,虽然服务可能允许它为undefined,但为了测试的完整性,最好补上,避免潜在的逻辑错误。
  • 验证Spy的调用状态:对于不需要触发HTTP请求的场景(比如输入值和savedValue相同、输入空字符串),可以添加expect(getSpy).not.toHaveBeenCalled()来验证逻辑分支的正确性。
  • 确保HttpServiceStub的正确性:你使用了HttpServiceStub,要确保这个Stub里实现了get$方法,返回一个合法的Observable,比如:
    export class HttpServiceStub {
      get$(restUrl: string, authToken: string, queryParams?: any[]): Observable<any> {
        return Observable.of({ data: null });
      }
    }
    
  • 初始化Store状态:你的服务在构造函数里订阅了Store的config.regex,测试时可以给Store设置初始状态,避免出现undefined的问题:
    beforeEach(() => {
      TestBed.configureTestingModule({
        imports: [
          StoreModule.forRoot({ 
            config: rootReducer.config,
            initialState: {
              config: {
                regex: { email: '', password: '' },
                apiBaseUrl: 'http://test-api.example.com'
              }
            }
          })
        ],
        providers: [{ provide: HttpService, useClass: HttpServiceStub }, ValidatorsService]
      });
    });
    

完整修正后的测试用例片段

describe('when checking if a record exists on the server', () => {
  let control, result, getSpy;

  beforeEach(() => {
    getSpy = spyOn(http, 'get$'); // 修正方法名
  });

  it('returns null if the user types the same value', done => {
    control = new FormControl('bob');
    result = service.recordExistsOnServer('user', 'username', 'bob', 'test-token');
    result(control).then(r => {
      expect(r).toEqual(null);
      expect(getSpy).not.toHaveBeenCalled(); // 验证未触发HTTP请求
      done();
    });
  });

  it('returns null if the user types an empty string', done => {
    control = new FormControl('');
    result = service.recordExistsOnServer('user', 'username', 'bob', 'test-token');
    result(control).then(r => {
      expect(r).toEqual(null);
      expect(getSpy).not.toHaveBeenCalled();
      done();
    });
  });

  it('returns null if the http call cannot find a record', fakeAsync(() => {
    getSpy.and.returnValue(Observable.of({ data: null }));
    control = new FormControl('bobby');
    result = service.recordExistsOnServer('user', 'username', 'bob', 'test-token');
    let validationResult;
    
    result(control).then(r => {
      validationResult = r;
    });

    tick(400);
    expect(validationResult).toEqual(null);
    expect(getSpy).toHaveBeenCalledWith('/users', 'test-token', [{ key: 'username', value: 'bobby' }]);
  }));

  it('returns { recordExistsOnServer: true } if the http call finds a record', fakeAsync(() => {
    getSpy.and.returnValue(Observable.of({ data: [{ id: 1, username: 'bobby' }] }));
    control = new FormControl('bobby');
    result = service.recordExistsOnServer('user', 'username', 'bob', 'test-token');
    let validationResult;
    
    result(control).then(r => {
      validationResult = r;
    });

    tick(400);
    expect(validationResult).toEqual({ recordExistsOnServer: true });
    expect(getSpy).toHaveBeenCalled();
  }));
});

按上面的修改调整后,你的测试应该就能正常通过了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:44:34