rxjs - 单元测试 NestJS Observable Http Retry
问题描述
我正在通过 NestJS 内置的 HttpService 向第 3 方 API 发出请求。我正在尝试模拟一个场景,其中对该 api 端点之一的初始调用可能会在第一次尝试时返回一个空数组。我想retryWhen
在延迟 1 秒后使用 RxJS 再次访问 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()
在正确的地方打电话。
解决方案
我认为问题在于它在发出retryWhen(notifier)
时会重新订阅同一个源。notifier
这意味着如果你有
new Observable(s => {
s.next(1);
s.next(2);
s.error(new Error('err!'));
}).pipe(
retryWhen(/* ... */)
)
每次重新订阅源时都会调用回调。在您的示例中,它将调用负责发送请求的逻辑,但不会post
再次调用该方法。
源可以被认为是Observable
的回调:s => { ... }
。
我认为您必须做的是根据是否发生错误有条件地选择来源。
也许你可以使用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)
}
})
)
推荐阅读
- javascript - 在变量内部的打字稿中验证值不是未定义的不起作用
- php - laravel markdown 电子邮件在收件箱中呈现 hrml
- javascript - 是否可以从 Cloudflare 工作人员重定向脚本中排除搜索机器人(如 Google)?
- mongodb - 对象数据转换的mongodb数组
- jquery - 本地运行光滑滑块失败
- angular - 如何扩展角度原理图属性?
- azure-devops-server-2019 - 作为 Visual Studio Enterprise 订阅者,我能否在不购买额外许可证的情况下在本地(生产)运行 DevOps Server 2019?
- ios - 在 ChildByAutoId 下从 Firebase 检索数据并添加到数组 - Xcode - Swift
- ms-access - 访问 - 字符串到日期 - 日期 + 时间
- python - python kivy - on_press 被执行两次