Skip to content
Advertisement

Testing actions reliably

I’m using xstate to implement a login flow. I have a machine where the initialState invokes a Promise, and if it’s rejected it will redirect to a state that has an entry action. I would like to test that the action is called at the right time properly.

machine.ts

{
  id: 'entrance',
  initial: States.FETCHING_SESSION,
  states: {
    [States.FETCHING_SESSION]: {
      invoke: {
        src: 'fetchSession',
        onError: {
          target: States.LOGGED_OUT,
        }
      }
    },
    [States.LOGGED_OUT]: {
      entry: ['navigateToLogin']
      type: 'final'
    },
  }
}

machine.spec.ts

    const mockFetchSession = jest.fn()
      .mockRejectedValueOnce({ error: new Error('401 unauthorized') })
    const mockNavigateToLogin = jest.fn()

    const service = interpret(entranceMachine.withConfig({
      services: {
        fetchSession: mockFetchSession
      },
      actions: {
        navigateToLogin: mockNavigateToLogin
      }
    }))

    it('Goes to login page on fail', (done) => {
      service.onTransition((state) => {
        expect(state.matches(States.LOGGED_OUT))
        expect(mockNavigateToLogin).toHaveBeenCalled() // <- this test case always fails
        done()
      })
      service.start()
    })

I managed to make it work in kinda of an ugly way by wrapping expect around a setTimout.

setTimeout(() => expect(mockNavigateToLogin).toHaveBeenCalled(), 100)

I wonder if there is a better option? Thanks

Advertisement

Answer

First, I suspect setTimeout just skips the expect..

I ran into the same issue using withConfig. The actions at least, are not called at all even if they appear in machine.options.actions.

My code looks like this:

const myAction = jest.fn();
const actions = { myAction };
const withCfg = machine.withConfig({
  actions,
});
const spy = jest
  .spyOn(actions, 'myAction')
  .mockImplementation(() => myAction());
// ...

expect(spy).toHaveBeenCalledTimes(1);

Note that in application those actions are executed just fine

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement