React + Redux-Observable Timeout Marble Testing

强颜欢笑 提交于 2019-12-12 18:24:09

问题


I am creating a web app using React and Redux Observables and I would like to create a unit test for the timeout scenario of one of my epics.

Here is the Epic:

export const loginUserEpic = (action$: ActionsObservable<any>, store, { ajax, scheduler }): Observable<Action> =>
  action$.pipe(
    ofType<LoginAction>(LoginActionTypes.LOGIN_ACTION),
    switchMap((action: LoginAction) =>
      ajax({
        url,
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: { email: action.payload.username, password: action.payload.password },
      }).pipe(
        timeout(timeoutValue, scheduler),
        map((response: AjaxResponse) => loginSuccess(response.response.token)),
        catchError((error: Error) => of(loginFailed(error))),
      ),
    ),
  );

And here is my test:

  it('should handle a timeout error', () => {
    // My current timeout value is 20 millseconds
    const inputMarble = '------a';
    const inputValues = {
      a: login('fake-user', 'fake-password'),
    };

    const outputMarble = '--b';
    const outputValues = {
      b: loginFailed(new Error('timeout')),
    };

    const ajaxMock = jest.fn().mockReturnValue(of({ response: { token: 'fake-token' } }));
    const action$ = new ActionsObservable<Action>(ts.createHotObservable(inputMarble, inputValues));
    const outputAction = loginUserEpic(action$, undefined, { ajax: ajaxMock, scheduler: ts });

    // I am not sure what error to expect here...
    ts.expectObservable(outputAction).toBe(outputMarble, outputValues);
    ts.flush();
    expect(ajaxMock).toHaveBeenCalled();
  });

What I expected is that the Epic would throw a timeout error, because my timeout value is of 20ms and the Observer is delaying 60ms before emmiting a value. I would then take this error and compare it in the end to make the test pass.

Unfortunately no timeout error is being thrown. Am I doing something wrong?


回答1:


You're using timeout in the chain after calling ajax() which returns just of({ ... }) (ajaxMock) so the timeout is never triggered because of emits immediately.

If you want to test the timeout operator you'll have add delay into ajaxMock:

const ajaxMock = () => of({ response: { token: 'fake-token' } }).pipe(delay(30, ts));

Here's your demo: https://stackblitz.com/edit/rxjs6-demo-pha4qp?file=index.ts

If the login request starts at 60 and you add 30 delay then at 80 you'll get a TimeoutError.

[0: Object
  frame: 80
  notification: Notification
  error: undefined
  hasValue: true
  kind: "N"
  value: Object
    error: Error
      message: "Timeout has occurred"
      name: "TimeoutError"
]


来源:https://stackoverflow.com/questions/53427965/react-redux-observable-timeout-marble-testing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!