如何测试 redux 可观察史诗的竞争条件
How to test race condition of a redux observable epic
我有一个用例,我需要取消 Ajax 调用并在史诗中做其他事情。 redux-observable 文档中有 an example 正好符合我的需要。但是,当我尝试测试史诗中的赛车条件时,"cancelation" 似乎不起作用。
示例代码如下:
import { ajax } from 'rxjs/ajax';
const fetchUserEpic = action$ => action$.pipe(
ofType(FETCH_USER),
mergeMap(action => race(
ajax.getJSON(`/api/users/${action.payload}`).pipe(
map(response => fetchUserFulfilled(response))
),
action$.pipe(
ofType(FETCH_USER_CANCELLED),
map(() => incrementCounter()),
take(1)
)
))
);
我的史诗与上面的例子有相同的结构,就像:
initViewsEpic = (action$, state$, { ajaxGet }) => action$
.ofType(INIT_VIEWS)
.pipe(
mergeMap(() => race(
ajaxGet('/api/views/...')
.pipe(
switchMap(response => of(
initViewsFulFilled(response),
taskCompleted(INIT_VIEWS),
)),
startWith(taskInProgress(INIT_VIEWS)),
catchError(error => of(
notification(),
taskCompleted(INIT_VIEWS),
)),
),
action$.pipe(
ofType(INIT_VIEWS_CANCEL),
map(() => taskCompleted(INIT_VIEWS),
take(1),
),
)),
);
我的测试是这样的:
test('should ignore the ajax call response when INIT_VIEWS_CANCEL action is fired', (done) => {
const action$ = ActionsObservable.of({ type: 'INIT_VIEWS' }, { type: 'INIT_VIEWS_CANCEL' });
const ajaxGet = () => timer(3000);
initViewsEpic(action$, state$, { ajaxGet })
.pipe(toArray()).subscribe((actions) => {
expect(actions).toEqual([
{
type: 'TASK_IN_PROGRESS',
payload: { id: 'INIT_VIEWS' },
},
{
type: 'TASK_COMPLETED',
payload: { id: 'INIT_VIEWS' },
},
]);
done();
});
});
我想由于 INIT_VIEWS_CANCEL
动作同步跟随 INIT_VIEWS
动作,它应该 "win" ajaxGet
并且不应该有任何 initViewsFulFilled
出去。但是这个测试的结果总是 returns initViewsFulFilled
作为我的史诗的第二个输出动作(我正在使用 jest 来测试史诗)。
我的测试有没有做错什么?如果是这样,我如何才能在 redux-observable 史诗中正确测试此竞争条件?
我会说我会给出一个测试 redux observable epics 的建议(那是
我所做的)——在 rxjs/testing
中使用 TestScheduler。这样我们就不用为了处理其他测试框架的 ascyn 事情而挠头了
希望这可以帮助到和我有同样问题的人。
我有一个用例,我需要取消 Ajax 调用并在史诗中做其他事情。 redux-observable 文档中有 an example 正好符合我的需要。但是,当我尝试测试史诗中的赛车条件时,"cancelation" 似乎不起作用。
示例代码如下:
import { ajax } from 'rxjs/ajax';
const fetchUserEpic = action$ => action$.pipe(
ofType(FETCH_USER),
mergeMap(action => race(
ajax.getJSON(`/api/users/${action.payload}`).pipe(
map(response => fetchUserFulfilled(response))
),
action$.pipe(
ofType(FETCH_USER_CANCELLED),
map(() => incrementCounter()),
take(1)
)
))
);
我的史诗与上面的例子有相同的结构,就像:
initViewsEpic = (action$, state$, { ajaxGet }) => action$
.ofType(INIT_VIEWS)
.pipe(
mergeMap(() => race(
ajaxGet('/api/views/...')
.pipe(
switchMap(response => of(
initViewsFulFilled(response),
taskCompleted(INIT_VIEWS),
)),
startWith(taskInProgress(INIT_VIEWS)),
catchError(error => of(
notification(),
taskCompleted(INIT_VIEWS),
)),
),
action$.pipe(
ofType(INIT_VIEWS_CANCEL),
map(() => taskCompleted(INIT_VIEWS),
take(1),
),
)),
);
我的测试是这样的:
test('should ignore the ajax call response when INIT_VIEWS_CANCEL action is fired', (done) => {
const action$ = ActionsObservable.of({ type: 'INIT_VIEWS' }, { type: 'INIT_VIEWS_CANCEL' });
const ajaxGet = () => timer(3000);
initViewsEpic(action$, state$, { ajaxGet })
.pipe(toArray()).subscribe((actions) => {
expect(actions).toEqual([
{
type: 'TASK_IN_PROGRESS',
payload: { id: 'INIT_VIEWS' },
},
{
type: 'TASK_COMPLETED',
payload: { id: 'INIT_VIEWS' },
},
]);
done();
});
});
我想由于 INIT_VIEWS_CANCEL
动作同步跟随 INIT_VIEWS
动作,它应该 "win" ajaxGet
并且不应该有任何 initViewsFulFilled
出去。但是这个测试的结果总是 returns initViewsFulFilled
作为我的史诗的第二个输出动作(我正在使用 jest 来测试史诗)。
我的测试有没有做错什么?如果是这样,我如何才能在 redux-observable 史诗中正确测试此竞争条件?
我会说我会给出一个测试 redux observable epics 的建议(那是
我所做的)——在 rxjs/testing
中使用 TestScheduler。这样我们就不用为了处理其他测试框架的 ascyn 事情而挠头了
希望这可以帮助到和我有同样问题的人。