redux-observable Promise 在单元测试中没有得到解决

Posted

技术标签:

【中文标题】redux-observable Promise 在单元测试中没有得到解决【英文标题】:redux-observable Promise is not getting resolved in unit test 【发布时间】:2018-10-31 10:06:39 【问题描述】:

我正在尝试测试这部史诗 https://github.com/zarcode/unsplashapp/blob/master/src/epics/photos.js 。问题是 map 在我运行测试时永远不会发生(我假设这意味着 Promise 永远不会解决),所以 photosSuccess 操作也永远不会发生:

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

但是,如果我这样做,requestAction.subscribe 承诺会得到解决,我会在控制台日志中得到结果。

注意:仅当我运行此测试https://github.com/zarcode/unsplashapp/blob/master/src/epics/photos.test.js 时才会发生这种情况,应用程序代码工作正常,数据获取正常。

问题是:如何正确编写这个测试?

【问题讨论】:

当我运行你的测试套件时,我得到了Invalid variable access: console 并且所有 6 个都失败了,你知道为什么吗? 您可以尝试将 return loadingAction 替换为 return Observable.fromPromise(loadingAction) 吗? @TarunLalwani 可能跟node版本有关,我是在v9.2.0上运行的 @dentemm photosActions.photosLoading(a.filter, a.refresh) 是一个对象,而不是一个 Promise,因此使用 rxjs v6 执行 return Observable.fromPromise(loadingAction) 或 from(loadingAction) 会引发此类错误:TypeError: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable. @zarcode,现在可以使用了,我在 10.1.0,可能有什么变化 【参考方案1】:

请记住,在测试异步代码时,您需要使用不同的策略,如 documentation 中所述。

在尝试测试我的异步代码之前,我遇到了很多问题,大多数问题是测试在处理已解决的承诺之前断言了预期的行为,并且需要在下一个滴答中进行断言event loop 的一部分,我实际上是通过将断言放在 setImmediate 中来实现的,这也可以通过 setTimeout 来实现。

从 jest 的文档中,您可以更改您的测试并通过 done 回调,正常调度触发操作,但将断言放在 setTimeout 回调中,超时时间为 1 毫秒,只是为了允许已解决的承诺要处理,然后将调用回调并断言所需的状态。

可以使用其他策略,例如async/await,或者您可以不使用setTimeout,而是解析一个空的Promise 并将断言放入then 回调中。请记住,如果测试流程中存在更多 Promise,则事件循环上的更多滴答声将是必要的,直到达到要断言的所需输出。

编辑:实现:

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

【讨论】:

您在问题中有 repo,而不是给出理论上的答案,如果您可以修复测试并在答案中更新相同的内容,那就太好了。我尝试了您的建议,但无法解决。所以最好只检查一次 我不同意“instead”,因为不解释问题发生的原因以及如何解决问题对我来说是没有意义的,这对每个人的学习都是最好的,包括对我来说。但是如果问题仍然存在,帮助更多也没问题,它实际上更快,我在pull request 中发布了解决方案。 正如我在答案中所写的那样,传递jest 公开的done 回调,在setImmediate 回调中断言并在其中也调用done,我'将更新答案以使实现更加直观。

以上是关于redux-observable Promise 在单元测试中没有得到解决的主要内容,如果未能解决你的问题,请参考以下文章

React + Redux-Observable + Typescript - 编译,参数不可分配错误

Redux-observable:当动作完成时组件如何订阅响应?

关于redux-observable的一点理解

在 catchError 之后不会执行 redux-observable

为啥我的 redux-observable 史诗没有被触发?

为啥使用 Redux-Observable 而不是 Redux-Saga?