Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

can't test Timers and/or cancelType #157

Open
lidoravitan opened this issue Feb 13, 2020 · 1 comment
Open

can't test Timers and/or cancelType #157

lidoravitan opened this issue Feb 13, 2020 · 1 comment

Comments

@lidoravitan
Copy link
Contributor

lidoravitan commented Feb 13, 2020

I created logic to verify something after 10 seconds, for instance:

export const validateSomething = createLogic({
  name: 'validateSomething',
  type: VALIDATE_SOMETHING,
  cancelType: CANCEL_VALIDATE_SOMETHING,
  process({ getState, action, cancelled$ }, dispatch, done) {
    const timeout = setTimeout(() => {
      const something = getSomeThing(getState())
      something ? dispatch(actionA()) : dispatch(actionB())
      done()
    }, 10000)

    cancelled$.subscribe(() => {
      clearTimeout(timeout)
      done()
    })
  }
})

as you can see I added cancelType which allows to cancel this logic before it reaches 10sec

I'm using Jest and redux-logic-test to test logics, However, there are two issues I ran into.

First, because whenComplete resolved only when done() is called. This means that the test should wait until the timers is over and only then call done() (at least 10 sec per unit-testing).

Ok, I know!...why don't use jest.useFakeTimers()? well, it looks like there's some problem with jest.useFakeTimers and some of the timers RxJs operators.

Anyway, useFakeTimers causes the test freeze until you invoke jest.runAllTimers() (it make sense why this happens, because jest.useFakeTimers() freeze all timers until you call jest.runAllTimers() or some another jest timer helpers) but now the test totally freezes because of the real timer(from the logic implementation) which wrap by jest.useFakeTimers() but created after the first jest.runAllTimers() so nothing happen.

Second, it related to the same problem with whenComplete which resolved only when done() is called. so imagine that I want to test cancelType, what I expect for?

a. create createMockStore,
b. dispatch 'VALIDATE_SOMETHING',
c. dispatch CANCEL_VALIDATE_SOMETHING

but when you dispatch the first action(in this case VALIDATE_SOMETHING), whenComplete wait until the done() will call and then resolved. it means you have to wait until the timer finish so you never cancel the logic in test.

Test code:

describe('test: unit tests', () => {
  test('should run properly', async () => {
    jest.useFakeTimers()

    const store = createMockStore({ logic: [validateSomething], reducer: rootReducer})

    store.dispatch({ type: 'VALIDATE_SOMETING' })

    jest.runAllTimers()

    const expectedActions = []
    await store.whenComplete()
    expect([]).toEqual(expectedActions)
  })
})

when using jest.useFakeTimers() and jest.runAllTimers() getting this error

javascript console.error node_modules/redux-logic/build-lib/createCancelled$.js:74
warning: logic (validateSegmentsPushDataArrivedBeforeTimeoutLogic) is still running after 60s, forget
to call done()? For non-ending logic, set warnTimeout: 0```
@lidoravitan lidoravitan changed the title can't test cancelType and/or Timers can't test Timers and/or cancelType Feb 13, 2020
@rmaenk
Copy link

rmaenk commented Aug 7, 2020

Hello,

It looks like you run into the same issue as me.
It is related to async dispatch from logic component.
If you are interesting, I have a redux-logic fork with some changes that supports dispatching action async and fixes issues related to wrong behavior of hooks. Also there are a lot of unit tests that may be helpful for you.
just use this line in your package.json (instead standard redux-logic) for check - does it help:
"@rmaenk/redux-logic": "git://github.com/rmaenk/redux-logic.git#v2.1.1-arfph.6",

sample of test: https://github.com/rmaenk/redux-logic/blob/async-ready-for-process-hook/test/createLogicMiddleware-process-async.spec.js. See the next test "describe(`first logic with delayed action and done"
for interrupting infinite test the next function is used:

/**
   * Interrupts async test hooks
   * @param {Function} hDone done callback of mocha test hook
   * @param {number} timeout time is ms for waiting before interrupt a mocha test hook
   * @param {object} mw redux-logic middleware,
   *  when specified then it expected that test hook will complete
   *  using mw.whenComplete before interrupting by timeout.
   * @returns {void}
   */
  function interrupt(hDone, timeout, mw) {
    // used to avoid double call of hDone callback
    let interrupted = false;
    const timer = setTimeout(() => {
      interrupted = true;
      hDone();
    }, timeout);

    if (mw) {
      mw.whenComplete(() => {
        // avoids double call of hDone callback
        clearTimeout(timer);
        if (!interrupted) {
          hDone();
        }
      });
    }
  }

Regards,
Roman

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants