redux-observable Promise is not getting resolved i

2019-04-18 20:14发布

问题:

I am trying to test this epic https://github.com/zarcode/unsplashapp/blob/master/src/epics/photos.js . Problem is that map never happens when I run test (which I assume means that Promise never resolves), so photosSuccess action never happens also:

export const loadPhotosToList = (action$: Observable<Action>, state$: 
Object): Observable<Action> => {
  const state = (photosFilter: PhotosFilter) => state$.value.photos[photosFilter];
  return action$
  // .ofType(ACTION.FETCH_PHOTOS_REQUESTED)
    .pipe(
      filter((a: Action) =>
        a.type === ACTION.FETCH_PHOTOS_REQUESTED &&
        ((state(a.filter).loadingState === 'idle' && !state(a.filter).isLastPage) || a.refresh)),
      switchMap((a) => {
        const nextPage = !a.refresh ? state(a.filter).lastLoadedPage + 1 : 1;
        const loadingAction = of(photosActions.photosLoading(a.filter, a.refresh));
        const request = api.fetchPhotos({
          page: nextPage,
          per_page: perPage,
          order_by: a.filter,
        });
        const requestAction = from(request)
          .pipe(
            // tap(data => { console.log("data", data); }),
            map(data =>
              photosActions.photosSuccess(
                data,
                a.filter,
                nextPage,
                data.length < perPage,
                a.refresh,
              )),
            catchError(e => of(photosActions.photosFail(e.message, a.filter))),
          );
        // requestAction.subscribe(x => console.log("-------",x));
        return loadingAction
          .pipe(
            concat(requestAction),
            takeUntil(action$
              .pipe(filter(futureAction => futureAction.type === ACTION.FETCH_PHOTOS_REQUESTED))),
          );
      }),
    );
};

However, if I do requestAction.subscribe promise gets resolved and I get the result in the console log.

Note: this happens only when I run this test https://github.com/zarcode/unsplashapp/blob/master/src/epics/photos.test.js, app code works fine, data is fetching fine.

Question is: How to write this test properly?

回答1:

Keep mind when testing async code you need to use different strategies, as noted in the documentation.

I crossed with a lot of problems before trying to test my async code, most of the problems were that the test was asserting the expected behavior before the resolved promise could be processed, and the assertion needed to be made in the next tick of the event loop, which I in practice achieve by putting the assertion inside a setImmediate, which can be achieved with a setTimeout too.

From the documentation of jest, you can change your test and pass the done callback, dispatch the triggering action as normal, but put the assertion inside a setTimeout callback with 1 ms timeout, just to allow the resolved promised to be processed, then the callback will be called and it will assert the desired state.

Other strategies can be used, like async/await, or you can instead of using setTimeout, resolve an empty Promise and put the assertion into the then callback. Just keep in mind that if more Promises exist within the tested flow, more ticks on the event loop will be necessary until the desired output is achieved to be asserted.

EDIT: Implementation:

// test
it('produces the photo model', (done) => {
...
// dispatch epic triggering action
store.dispatch(...);
// assertion
setImmediate(() => {
  expect(store.getActions()).toEqual([
  ...
  ]);
  done();
});