我有一个redux-observable epic,用于轮询端点,获取进度更新,直到进度为100%。使用debounceTime实现轮询间隔,如下所示:
function myEpic(action$, store, dependencies) {
return action$.ofType('PROCESSING')
.do(action => console.log(`RECEIVED ACTION: ${JSON.stringify(action)}`))
.debounceTime(1000, dependencies.scheduler)
.mergeMap(action => (
dependencies.ajax({ url: action.checkUrl })
.map((resp) => {
if (parseInt(resp.progress, 10) === 100) {
return { type: 'SUCCESS' };
}
return { checkUrl: resp.check_url, progress: resp.progress, type: 'PROCESSING' };
})));
}
这很好但是我想编写一个集成测试,当进度为25%,然后是50%,然后是100%时,测试商店的状态。
在我的集成测试中,我可以将dependencies.scheduler
设置为new VirtualTimeScheduler()
。
这就是我现在尝试这样做的方式(使用jest):
describe('my integration test', () => {
const scheduler = new VirtualTimeScheduler();
beforeEach(() => {
// Fake ajax responses
const ajax = (request) => {
console.log(`FAKING REQUEST FOR URL: ${request.url}`);
if (request.url === '/check_url_1') {
return Observable.of({ progress: 25, check_url: '/check_url_2' });
} else if (request.url === '/check_url_2') {
return Observable.of({ progress: 50, check_url: '/check_url_3' });
} else if (request.url === '/check_url_3') {
return Observable.of({ progress: 100 });
}
return null;
};
store = configureStore(defaultState, { ajax, scheduler });
});
it('should update the store properly after each call', () => {
store.dispatch({ checkUrl: '/check_url_1', progress: 0, type: 'PROCESSING' });
scheduler.flush();
console.log('CHECK CORRECT STATE FOR PROGRESS 25');
scheduler.flush();
console.log('CHECK CORRECT STATE FOR PROGRESS 50');
scheduler.flush();
console.log('CHECK CORRECT STATE FOR PROGRESS 100');
});
});
我的预期输出是:
RECEIVED ACTION: {"checkUrl":"/check_url_1","progress":0,"type":"PROCESSING"}
FAKING REQUEST FOR URL: /check_url_1
CHECK CORRECT STATE FOR PROGRESS 25
RECEIVED ACTION: {"checkUrl":"/check_url_2","progress":25,"type":"PROCESSING"}
FAKING REQUEST FOR URL: /check_url_2
CHECK CORRECT STATE FOR PROGRESS 50
RECEIVED ACTION: {"checkUrl":"/check_url_3","progress":50,"type":"PROCESSING"}
# CHECK CORRECT STATE FOR PROGRESS 100
但我获得的输出是
RECEIVED ACTION: {"checkUrl":"/check_url_1","progress":0,"type":"PROCESSING","errors":null}
FAKING REQUEST FOR URL: /check_url_1
RECEIVED ACTION: {"checkUrl":"/check_url_2","progress":25,"type":"PROCESSING","errors":null}
CHECK CORRECT STATE FOR PROGRESS 25%
CHECK CORRECT STATE FOR PROGRESS 50%
CHECK CORRECT STATE FOR PROGRESS 100%
此时测试结束。我正在配置商店,以便我可以像推荐的here一样模拟ajax请求和用于debounceTime的调度程序
所以我的问题是如何在三个ajax请求中的每一个之后测试我的商店的状态?
答案 0 :(得分:2)
有趣的是,我玩了你的代码并相当自信你刚刚在debounceTime
运算符中发现了一个错误,导致明显吞噬预定的去抖动。坏消息是,即使修复了这个错误,你的代码仍然不会按顺序执行。
忍耐我,因为狗屎即将成真:
scheduler.flush()
和VirtualScheduler执行预定的去抖动工作,该工作会将原始处理操作传递给mergeMap
debounceTime
的 rxjs代码然后设置this.lastValue = null
and this.hasValue = false
这是rxjs错误,需要在进入目的地之前完成 scheduler.flush()
ONCE,这是我们此时所处的功能。this.hasValue === false
因为第一个预定的去抖动设置它,所以debounceTime运算符不会发出任何内容。scheduler.flush()
console.log('CHECK CORRECT STATE FOR PROGRESS 25')
scheduler.flush()
来电都没有,因为没有安排任何事情。这在技术上是一个错误,但是没有人遇到它并不奇怪,因为同步地运行去抖动而没有任何延迟使得它失败,除非你正在测试,当然。 This ticket is basically the same thing和OJ说RxJS没有提供重入保证,但在这种情况下我可能会讨论。 I've filed a PR with the fix to discuss
请记住,这个错误不会解决你关于排序的潜在问题,但是会阻止这些行为被吞噬。
如果你想保持100%的同步行为(VirtualScheduler),我不知道如何做你想要做的事情。在去抖之间你需要一些屈服于你的测试的方法。对我来说当我编写集成测试的时候,如果有的话,我会嘲笑的很少。例如让debounces自然地或者通过模拟setTimeout来更快地推进它们,但是仍然保持它们异步,这将导致你的测试结果允许你检查状态,但是你的测试也是异步的。 / p>
对于任何想要复制的人,here's the StackBlitz code I used
答案 1 :(得分:0)
答案是异步重写测试。同样值得注意的是,我必须通过返回Observable.fromPromise
而不仅仅是常规Observable.of
来模拟ajax请求,否则他们仍然会被去抖动吞噬。沿着这些方向的东西(使用jest):
describe('my integration test', () => {
const scheduler = new VirtualTimeScheduler();
beforeEach(() => {
// Fake ajax responses
const ajax = request => (
Observable.fromPromise(new Promise((resolve) => {
if (request.url === '/check_url_1') {
resolve({ response: { progress: 25, check_url: '/check_url_2' } });
} else if (request.url === '/check_url_2') {
resolve({ response: { progress: 50, check_url: '/check_url_3' } });
} else {
resolve({ response: { progress: 100 } });
}
}))
);
store = configureStore(defaultState, { ajax, timerInterval: 1 });
});
it('should update the store properly after each call', (done) => {
let i = 0;
store.subscribe(() => {
switch (i) {
case 0:
console.log('CHECK CORRECT STATE FOR PROGRESS 0');
break;
case 1:
console.log('CHECK CORRECT STATE FOR PROGRESS 25');
break;
case 2:
console.log('CHECK CORRECT STATE FOR PROGRESS 50');
break;
case 3:
console.log('CHECK CORRECT STATE FOR PROGRESS 100');
done();
break;
default:
}
i += 1;
});
store.dispatch({ checkUrl: '/check_url_1', progress: 0, type: 'PROCESSING' });
});
});
我还通过将定时器间隔作为依赖项将其设置为1。在我的史诗中我设置如下:.debounceTime(dependencies.timerInterval || 1000)