【问题标题】:How to create a mock observable to test http rxjs retry/retryWhen in Angular?如何在 Angular 中创建一个模拟 observable 来测试 http rxjs retry/retryWhen?
【发布时间】:2018-12-26 05:51:25
【问题描述】:

我的 Angular 应用程序中有一个这种形式的 http 请求:this.http.get(url,options).pipe(retry(5))

我想使用 jasmine 对此进行单元测试,这样 http 请求首先会返回错误,但在随后的尝试中,会返回预期的数据。

我之前按照 angular.io 文档 spyOn(http,'get').and.returnValue(defer(() => Promise.reject(errorObject))); 的建议以这种格式返回了 http 错误

rxjs操作符重试好像没有再次调用http.get函数,所以改变spy的返回值不起作用。我认为我需要做的是以某种方式从间谍返回一个 observable,它首先发出错误,然后发出数据。我考虑过使用 BehaviorSubject,但不认为它接受要传递的错误。

有什么方法可以实现吗?

【问题讨论】:

    标签: angular jasmine rxjs


    【解决方案1】:

    你能试试下面的方法吗?您还可以发布该功能的完整代码吗?

    it('should check for the get error call ', () => {
                spyOn(http, 'get').and.returnValue(Observable.throw('error'));
                fixture.detectChanges() // or component.function()
                expect(http.get).toHaveBeenCalled();
            });
    

    【讨论】:

    • 谢谢,但我已经能够使用上面的代码抛出错误 spyOn(http,'get').and.returnValue(defer(() => Promise.reject(errorObject))) ;我想要做的是在第一次尝试时抛出错误,但在使用 rxjs 重试时,它应该得到实际数据
    • 听起来不错,请回答您自己的问题,以便对其他人有所帮助
    • 谢谢,但您可能误解了我的问题?我关心的是测试 http 调用的重试,而不是第一次调用
    • 哦,对不起
    【解决方案2】:

    如果我们转到 angular source code,我们将看到下一个:

    // Start with an Observable.of() the initial request, and run the handler (which
    // includes all interceptors) inside a concatMap(). This way, the handler runs
    // inside an Observable chain, which causes interceptors to be re-run on every
    // subscription (this also makes retries re-run the handler, including interceptors).
    const events$: Observable<HttpEvent<any>> =
        of (req).pipe(concatMap((req: HttpRequest<any>) => this.handler.handle(req)));
    

    为了使请求可重试,他们以 of(req) 开头,因此我们可以在单元测试中模拟这种行为。例如:

    spyOn(http,"get").and.returnValue(
        createRetriableStream(
           throwError("err"),
           throwError("err"),
           throwError("err"),
           of("a")
        )
    );
    

    createRetriableStream 函数可能如下所示:

    function createRetriableStream(...resp$: any): any {
       let fetchData: Spy = createSpy("fetchData");
       fetchData.and.returnValues(...resp$);
       return of(undefined).pipe(switchMap((_) => fetchData()));
    }
    

    通常,重试逻辑与尝试之间的延迟一起使用,要涵盖这部分,您可以使用time progression syntax。所以你的测试可能看起来像:

        it("throw error after 2 retries", () => {
            testScheduler.run(({ cold, expectObservable }) => {
                source$ = createRetriableStream(cold("-#"), cold("-#"), cold("-#"),cold("-3")).pipe(
                    retryWhen(
                       return errors.pipe(
                           mergeMap((err) => {                
                               return 2 > repeatCount
                                   ? throwError(err)
                                   : timer(100, testScheduler);             
                           })
                      );)
                 );
            expectObservable(source$).toBe("- 200ms --#");
      });
    

    【讨论】:

    • 谢谢,这看起来很有效。
    猜你喜欢
    • 2020-02-26
    • 2020-08-30
    • 2017-04-06
    • 1970-01-01
    • 2018-01-14
    • 1970-01-01
    • 2023-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多