16

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?

6
  • When I run your test suite, I get Invalid variable access: console and all 6 fails, you know why? Commented May 24, 2018 at 10:25
  • Could you try replacing return loadingAction with return Observable.fromPromise(loadingAction)? Commented May 24, 2018 at 17:34
  • @TarunLalwani maybe it has something to do with the node version, I am running it on v9.2.0 Commented May 25, 2018 at 6:35
  • @dentemm photosActions.photosLoading(a.filter, a.refresh) is an object, not a Promise so doing return Observable.fromPromise(loadingAction) or from(loadingAction) with rxjs v6 throws an error of this kind: TypeError: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable. Commented May 25, 2018 at 6:41
  • @zarcode, works now, I was on 10.1.0, may be something has changed in that Commented May 25, 2018 at 7:50

1 Answer 1

3
+150

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();
});
Sign up to request clarification or add additional context in comments.

3 Comments

You have the repo in the question, instead of giving a theoretical answer, it would be great if you could just fix the test and update the same in your answer. I tried your recommendation and not able to fix it. So its best if you just check it once
I don't agree with "instead", because there's no point for me in fixing the problem without explaining why the problem is happening and how to fix it, this is the best for everyone's learning, including to me. But no problem in helping more if the problem persists, it's actually faster, and I posted the solution in this pull request.
And as exactly as I've written in the answer, pass the done callback which jest exposes, assert inside the setImmediate callback and call done inside it too, I'll update the answer to show the implementation to be more visual.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.