Jest.js 如何测试redux observable epic的竞态条件

xvw2m8pv  于 2023-05-27  发布在  Jest
关注(0)|答案(1)|浏览(196)

我有一个用例,我需要取消 AJAX 调用并在Epic中执行其他操作。在redux-observable文档中有an example,它完全符合我的需要。然而,当我试图在我的史诗测试赛车条件,“取消”似乎不工作。
示例代码如下所示:

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)
    )
  ))
);

我的epic与上面的例子有相同的结构,就像:

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动作,它应该“赢得”ajaxGet,并且不应该有任何initViewsFulFilled出去。但是这个测试的结果总是返回initViewsFulFilled作为我的epic的第二个输出动作(我正在使用jest测试epic)。
我在考试中做错了什么吗?如果是这样的话,我如何在一个还原可观察的史诗中正确地测试这个竞争条件?

js5cn81o

js5cn81o1#

我会说我将给予一个测试redux observable epics的建议(这就是我所做的)-使用TestScheduler in rxjs/testing。这样我们就不必为处理其他测试框架的不好的东西而挠头了
下面是伪代码:

import { of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { ActionsObservable } from 'redux-observable';
import { TestScheduler } from 'rxjs/testing';
import { initViewsEpic } from '../actions';

const deepEquals = (actual, expected) => expect(actual).toEqual(expected);
const createTestScheduler = () => new TestScheduler(deepEquals);
let ts = {};

beforeEach(() => {
  ts = createTestScheduler();
});

afterEach(() => {
  ts.flush();
});

describe('initViewsEpic Epic', () => {
  const state$ = of(...);

  let values = {};

  beforeEach(() => {
    values = {
      a: { type: 'INIT_VIEWS' },
      b: { type: 'INIT_VIEWS_FULFILLED'},
      c: { type: 'INIT_VIEWS_CANCEL' },
      d: { type: 'TASK_IN_PROGRESS', payload: { id: 'INIT_VIEWS' } },
      e: { type: 'TASK_COMPLETED', payload: { id: 'INIT_VIEWS' } },
    };
  });

  test('in case of success') ...
  test('in case of error') ...

  test('in case of cancel', () => {
    const source = ActionsObservable.from(ts.createHotObservable('-ac----', values));
    const ajaxGet = () => () =>
      of({ response: 'arbitrary obj' }).pipe(delay(ts.createTime('---|'), ts));

    const actual = initViewsEpic(source, state$, { ajaxGet });

    ts.expectObservable(actual).toBe('-de---', values);
  });
});

希望这能帮助到和我有同样问题的人。

相关问题