Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test nested Actions in a Vue application?

I have a TypeScript-based Vue project with Jest as a test framework. I have Actions in a Module which I am trying to test.

My Actions look like this:

  @Action({})
  saveSomeData (payload: any): Promise<any> {
    const { isActive, id, routes } = payload
    return this.context.dispatch('otherModule/createId', '', { root: true })
        .then((id: string) => {
          payload = { isActive, id, routes, type: 'postRoutes' }
          return this.context.dispatch('submitRoutes', payload)
        })
  }

  @Action({})
  submitRoutes (payload: any): Promise<any> {
    const { isActive, id, routes, type } = payload
    return ActiveService.setActive(id)
        .then(() => this.context.dispatch(type, { id, routes }))
  }

Here is how my test looks like:

// Mocking createId in otherModule module to return ID
jest.mock('@/store/modules/otherModule', () => ({
  createId: jest.fn(() => Promise.resolve([
    {
      id: 'someId'
    }
  ]))
}))

...

describe('Testing save MyModule data', () => {
    let store: any

    beforeEach(() => {
      store = new Vuex.Store({
        modules: {
          myModule,
          otherModule
        }
      })
    })

    test('Should call createId and then call submitRoutes if ID is empty', async () => {
      const payload = {
        isActive: true,
        id: '',
        routes: []
      }
      const pld = {
        isActive: true,
        id: 'someId',
        routes: [],
        type: 'postRoutes'
      }

      store.dispatch = jest.fn()
      await store.dispatch('myModule/saveSomeData', payload)
      expect(store.dispatch).toHaveBeenCalledWith('myModule/saveSomeData', payload)
      expect(store.dispatch).toHaveBeenCalledWith('otherModule/createId') // <-- here I get an error
      expect(store.dispatch).toHaveBeenCalledWith('myModule/submitRoutes', pld)
    })
  })

Problem: My test fails and I haven't found any way to make it work.

The error:

Error: expect(jest.fn()).toHaveBeenCalledWith(...expected)

Expected: "otherModule/createId"
Received: "myModule/saveSomeData", {"id": "", "isActive": true, "routes": []}

Number of calls: 1

What I've tried

I've followed the Vuex documentation together with Jest, I also tried different solutions from the internet - unfortunately no luck.

I would appreciate any help.

like image 480
I. Shpota Avatar asked Apr 09 '20 10:04

I. Shpota


1 Answers

store.dispatch = jest.fn() makes dispatch function a no-op, it isn't expected to call saveSomeData and, therefore, dispatch other actions.

This assertion isn't useful because it basically tests the previous line:

expect(store.dispatch).toHaveBeenCalledWith('myModule/saveSomeData', payload)

store.dispatch spy or stub isn't supposed to affect context.dispatch in actions because the context is created on store initialization and already uses original dispatch. It may be unnecessary to do this because these are actions that should be tested, not Vuex itself.

Actions can be spied on module level with jest.mock and jest.requireActual, or locally on Vuex module object if necessary. Module spies and mocks should happen on top level. Object spies and mocks should happen prior to store instantiation.

In this case a tested units are myModule actions, ActiveService.setActive and otherModule/createId can be considered different units and should be mocked. If postRoutes contains side effects, they can be mocked as well.

jest.spyOn(otherModule.actions, 'createId');
jest.spyOn(ActiveService, 'setActive');
store = new Vuex.Store(...)

...

otherModule.actions.createId.mockValue(Promise.resolve('stubbedId'));
ActiveService.setActive.mockValue(Promise.resolve());
await store.dispatch('myModule/saveSomeData', payload)
// assert otherModule.actions.createId call
// assert ActiveService.setActive call
// assert otherModule.actions.postRoutes call
like image 73
Estus Flask Avatar answered Nov 15 '22 09:11

Estus Flask