【问题标题】:Unit testing NestJS Observable Http Retry单元测试 NestJS Observable Http Retry
【发布时间】:2020-08-30 07:57:32
【问题描述】:

我正在通过 NestJS 的内置 HttpService 向第 3 方 API 发出请求。我正在尝试模拟一个场景,其中对该 api 端点之一的初始调用可能会在第一次尝试时返回一个空数组。我想在延迟 1 秒后使用 RxJS 的 retryWhen 再次访问 api。但是,我目前无法让单元测试模拟第二个响应:

it('Retries view account status if needed', (done) => {
    jest.spyOn(httpService, 'post')
      .mockReturnValueOnce(of(failView)) // mock gets stuck on returning this value
      .mockReturnValueOnce(of(successfulView));
    const accountId = '0812081208';
    const batchNo = '39cba402-bfa9-424c-b265-1c98204df7ea';
    const response =client.viewAccountStatus(accountId, batchNo);
    response.subscribe(
      data => {
        expect(data[0].accountNo)
          .toBe('0812081208');
        expect(data[0].companyName)
          .toBe('Some company name');
        done();
      },
    )
  });

我的实现是:

viewAccountStatus(accountId: string, batchNo: string): Observable<any> {
    const verificationRequest = new VerificationRequest();
    verificationRequest.accountNo = accountId;
    verificationRequest.batchNo = batchNo;


    this.logger.debug(`Calling 3rd party service with batchNo: ${batchNo}`);

    const config = {
      headers: {
        'Content-Type': 'application/json',
      },
    };

    const response = this.httpService.post(url, verificationRequest, config)
      .pipe(
        map(res => {
          console.log(res.data); // always empty
          if (res.status >= 400) {
            throw new HttpException(res.statusText, res.status);
          }

          if (!res.data.length) {
            this.logger.debug('Response was empty');
            throw new HttpException('Account not found', 404);
          }

          return res.data;
        }),
        retryWhen(errors => {
          this.logger.debug(`Retrying accountId: ${accountId}`);
          // It's entirely possible the first call will return an empty array
          // So we retry with a backoff
          return errors.pipe(
            delayWhen(() => timer(1000)),
            take(1),
          );
        }),
      );

    return response;
  }

从初始地图内部登录时,我可以看到数组始终为空。就好像第二个模拟值永远不会发生。也许我对可观察对象的工作方式也有一个严重的误解,我应该以某种方式尝试断言发出的 SECOND 值?无论如何,当 observable 重试时,我们应该看到第二个模拟值,对吧?

我也来了

: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Error:

每次运行...所以我猜我没有在正确的地方打电话给done()

【问题讨论】:

    标签: rxjs jestjs nestjs


    【解决方案1】:

    我认为问题在于retryWhen(notifier) 将在其notifier 发出时重新订阅相同的源。

    意思是如果你有

    new Observable(s => {
      s.next(1);
      s.next(2);
    
      s.error(new Error('err!'));
    }).pipe(
      retryWhen(/* ... */)
    )
    

    每次重新订阅源时都会调用回调。在您的示例中,它将调用负责发送请求的逻辑,但不会再次调用post 方法。

    源可以被认为是Observable的回调:s =&gt; { ... }

    我认为您必须做的是根据是否发生错误有条件地选择源。

    也许你可以使用mockImplementation:

    let hasErr = false;
    
    jest.spyOn(httpService, 'post')
      .mockImplementation(
        () => hasErr ? of(successView) : (hasErr = true, of(failView))
      )
    

    编辑

    我认为上面的内容没有什么不同,我认为mockImplementation 应该是什么样子:

    let err = false;
    
    mockImplementation(
     () => new Observable(s => {
       if (err) { 
         s.next(success) 
       } 
       else { 
        err = true;
        s.next(fail) 
       } 
     })
    )
    

    【讨论】:

    • 我认为它肯定在“重新订阅”部分。话虽如此,在现实生活中必须再次调用 POST,所以我需要找到一些方法来实现这一点。我确实尝试了您的建议,但它与调用.mockReturnValueOnce() 具有相同的效果。该实现确实运行了多次,它永远不会到达第二个“successfulView”。因为我无法证明这会做我认为应该做的事情,所以很遗憾我不能发货。我将恢复到基于 Promise 的解决方案,并了解更多关于 Observables 的信息。
    • 在你这样做之前,试试这个:mockImplementation(() =&gt; new Observable(s =&gt; { if (err) { s.next(success) } else { err = true; s.next(fail) } })).
    • 通过了。我担心的是它实际上不会再次调用 POST。在真实场景中,我想发布/重试端点。我当然感谢您的回答,因为它可以帮助我理解更多。秘密真的在next()
    • 想象一下http.post 只是返回new Observable(s =&gt; fetch(...)then(...).then(r =&gt; s.next(r)))。 observable 的回调是 sourceretryWhen 将重新订阅该流...这就是为什么不再调用 post 方法的原因。在真实场景中,虽然没有再次调用该方法,但它重新调用了该方法创建的逻辑,所以本质上是一样的。
    • 另外,如果您想了解更多关于HttpClientModule 及其工作原理的信息,您可以查看this article
    猜你喜欢
    • 2019-07-16
    • 2018-12-26
    • 2019-11-22
    • 2018-09-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-24
    • 1970-01-01
    • 2021-05-29
    相关资源
    最近更新 更多