Rxjs 单元测试NestJS可观察Http重试

Rxjs 单元测试NestJS可观察Http重试,rxjs,jestjs,nestjs,Rxjs,Jestjs,Nestjs,我正在通过NestJS的内置HttpService向第三方API发出请求。我试图模拟这样一个场景:对这个api的一个端点的初始调用可能会在第一次尝试时返回一个空数组。我想使用RxJS的retryWhen在延迟1秒后再次点击api。我目前无法让单元测试模拟第二个响应,但是: it('Retries view account status if needed', (done) => { jest.spyOn(httpService, 'post') .mockReturnV

我正在通过NestJS的内置HttpService向第三方API发出请求。我试图模拟这样一个场景:对这个api的一个端点的初始调用可能会在第一次尝试时返回一个空数组。我想使用RxJS的
retryWhen
在延迟1秒后再次点击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;
  }

每次跑步时。。。所以我猜我没有在正确的位置调用
done()

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

意思是如果你有

新的可观察对象(s=>{
s、 下一步(1);
s、 其次(2);
s、 错误(新错误('err!'));
}).烟斗(
retryWhen(/*…*/)
)
每次重新订阅源时都会调用回调。在您的示例中,它将调用负责发送请求的逻辑,但不会再次调用
post
方法

源代码可以被认为是
可观察的
的回调:
s=>{…}

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

也许你可以使用:

让hasErr=false;
jest.spyOn(httpService,'post')
A.实施(
()=>hasErr?of(成功视图):(hasErr=true,of(失败视图))
)
编辑 我认为上面没有什么不同,我认为
mockImplementation
应该是什么样子的:

让err=false;
模拟实现(
()=>新的可观测数据=>{
如果(错误){
s、 下一步(成功)
} 
否则{
错误=正确;
s、 下一个(失败)
} 
})
)

我想肯定是在“重新订阅”部分。话虽如此,在现实生活中,这篇文章将不得不再次被调用,所以我需要找到一些方法来实现这一点。我确实尝试了您的建议,但它与调用
.mockReturnValueOnce()
的效果相同。该实现确实运行了多次,但它永远不会到达第二个“successfulView”。因为我不能证明这会做我认为它应该做的事情,我不能很遗憾地发货。我将返回到一个基于承诺的解决方案,并了解有关可观察对象的更多信息。在此之前,请尝试以下操作:
mockImplementation(()=>newobservate(s=>{if(err){s.next(success)}else{err=true;s.next(fail)}))
。我担心的是,它实际上不会再给邮局打电话了。在实际场景中,我希望发布/重试端点。我当然很感激你的回答,因为它能帮助我理解更多。真正的秘密在于
next()
想象一下
http.post
只返回
newobservable(s=>fetch(…),然后(…)。然后(r=>s.next(r))
。可观察对象的回调是源
retryWhen
将重新订阅该流。。。这就是为什么不再调用
post
方法的原因。在实际场景中,虽然它不会再次调用该方法,但它会重新调用该方法创建的逻辑,因此本质上是一样的。此外,如果您想了解更多关于
HttpClientModule
及其工作原理的信息,可以查看。
: 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: