reduxredux-observable

Redux-Observable testing readonly epics


I would like to test the following epic:

authEpics.logoutEpic = function (action$, _, deps) {
  return action$.pipe(
    ofType(authActions.logout),
    tap(() => {
      const { history, browser } = deps;
      browser.localStorage.removeItem('user');
      history.push(ROUTES.LOGIN);
    }),
    ignoreElements()
  );
};

Here's the test case:

describe('Logout Epic', () => {
  test('should remove user from localStorage', (done) => {
    scheduler.run((helpers) => {
      const { hot, expectObservable } = helpers;
      const action$ = hot('a', {
        a: authActions.logout(),
      });
      const deps = testUtils.mockEpicDependencies();
      const output$ = authEpics.logoutEpic(action$, null, deps);
      
      // Statement 1.
      expectObservable(output$).toBe('');

      // Statement 2.
      output$.subscribe({
        complete: () => {
          expect(deps.browser.localStorage.removeItem).toHaveBeenCalledWith('user');
          expect(deps.history.push).toHaveBeenCalledWith(ROUTES.LOGIN);
          done();
        }
      });
    });
});

Based on the definition of ignoreElements, I am not able grok the following observations:

  1. In Statement 1, I was of the opinion expectObservable(output$).toBe('|') should work, but it didn't. Instead expectObservable(output$).toBe('') works.
  2. In Statement 2, the complete function never gets called.

Solution

  • To make the test case work, I had to flush the promise queue (and not use expectObservable). Here's the revised test case:

    describe('Logout Epic', () => {
      test('should remove user from localStorage', (done) => {
        scheduler.run(async (helpers) => {
          const { hot } = helpers;
          const action$ = hot('a', {
            a: authActions.logout(),
          });
          const deps = testUtils.mockEpicDependencies();
          const output$ = authEpics.logoutEpic(action$, null, deps);
    
          // *** Flush the promise queue ***
          await new Promise((resolve) => resolve());
    
          expect(deps.browser.localStorage.removeItem).toHaveBeenCalledWith('user');
          expect(deps.history.push).toHaveBeenCalledWith(ROUTES.LOGIN);
    
          done();
        });
    });