Unit testing a Vuex store

Edd Yerburgh

@eddyerburgh

 

export const sum = (a, b) => a + b

Unit test

import sum from './sum'

test('returns sum of input', () => {

})
import sum from './sum'

test('returns sum of input', () => {
  expect(sum(1,3)).toBe(4)
})

Why?

  • Verify code works

  • Document code

  • Enable workflows

Components

Actions

State

Vuex

Actions

Mutations

State

Getters

Vuex store

A web app

Actions

Mutations

State

Getters

fetchItems

setItems

items

displayItems

Three approaches..

1. Don't

  • Unit tests take time to write

  • e2e tests test store implicitly

2. Test store parts seperately

Actions

Mutations

State

Getters

export default {
  setItems (state, { items }) {
    state.items = items
  }
}

Mutation

export default {
  // ..
  setItems (state, { items }) {
    state.items = items
  },
  // ..
}
import mutations from './mutations'

test('setItems mutates state.items', () => {






})
import mutations from './mutations'

test('setItems mutates state.items', () => {
  const state = {
    items: []
  }
  const items = [{}, {}]
  mutations.setItems(state, { items })
  expect(state.items).toBe(items)
})
import mutations from './mutations'

test('setItems mutates state.items', () => {
  const state = {
    items: []
  }



})
import mutations from './mutations'

test('setItems mutates state.items', () => {
  const state = {
    items: []
  }
  const items = [{}, {}]


})
import mutations from './mutations'

test('setItems mutates state.items', () => {
  const state = {
    items: []
  }
  const items = [{}, {}]
  mutations.setItems(state, { items })

})

1. Call with state object and payload

2. Assert state value updated

Mutations

displayItems ({ page, items }) {
  const perPage = 20
  const index = perPage * page
  return items.slice(index - perPage, index)
},

Getter

test('returns items 21-40 if state.page value is 2', () => {





  
  
  
  
  
  
  
})
test('returns items 21-40 if state.page value is 2', () => {
  const items = Array(42)
    .fill()
    .map((v, i) => ({ i }))

  const state = {
    items,
    page: 2
  }

  
  
  
})
test('returns items 21-40 if state.page value is 2', () => {
  const items = Array(42)
    .fill()
    .map((v, i) => ({ i }))

  const state = {
    items,
    page: 2
  }

  const result = getters.displayItems(state)


})
test('returns items 21-40 if state.page value is 2', () => {
  const items = Array(42)
    .fill()
    .map((v, i) => ({ i }))

  const state = {
    items,
    page: 2
  }

  const result = getters.displayItems(state)

  expect(result).toEqual(items.slice(20, 40))
})

1. Call with state object, (resolved getters)

2. Assert return value

Getters

import { fetchData } from '../api'

export default {
  fetchItems ({ commit }) {
    return fetchData()
      .then(items => commit('setItems', { items }))
  }
}

Action

import actions from './actions'
import { fetchData } from '../api'

jest.mock('../api')

test('commits items returned by api method', async () => {




  


})
import actions from './actions'
import { fetchData } from '../api'

jest.mock('../api')

test('commits items returned by api method', async () => {
  const items = [1, 2, 3]
  fetchData.mockResolvedValue(items)
  const commit = jest.fn()
  await actions.fetchItems({ commit })
  expect(commit).toHaveBeenCalledWith(
    'setItems', { items }
  )
})
import actions from './actions'
import { fetchData } from '../api'

jest.mock('../api')

test('commits items returned by api method', async () => {
  const items = [1, 2, 3]
  fetchData.mockResolvedValue(items)





})
import actions from './actions'
import { fetchData } from '../api'

jest.mock('../api')

test('commits items returned by api method', async () => {
  const items = [1, 2, 3]
  fetchData.mockResolvedValue(items)
  const commit = jest.fn()




})
import actions from './actions'
import { fetchData } from '../api'

jest.mock('../api')

test('commits items returned by api method', async () => {
  const items = [1, 2, 3]
  fetchData.mockResolvedValue(items)
  const commit = jest.fn()
  await actions.fetchItems({ commit })



})

1. Call with mocked store context, (payload)

2. Assert that action commits data

Actions

Testing store parts seperately

  • Fine-grained
  • Brittle

3. Test a store instance

Actions

Mutations

State

Getters

store

fetchItems

getters.displayItems

1. Install Vuex on Vue constructor

2. Create a store instance

3. Test instance

Steps

Step 1

Install Vuex

Vue.use(Vuex)

Vue

new Vue({})

new Vue({})

Vue.use

Vue

new Vue({})

new Vue({})

localVue.use

localVue

Step 1

Install Vuex

import { createLocalVue } from '@vue/test-utils'

const localVue = createLocalVue()

(on localVue constructor)

import { createLocalVue } from '@vue/test-utils'

const localVue = createLocalVue()
localVue.use(Vuex)
const store = new Vuex.Store({
  state: {
    items: []
  },
  actions,
  mutations
})
const storeConfig = createStoreConfig()

const store = new Vuex.Store(storeConfig)

Step 2

Create instance

import mutations from './mutations'
import actions from './actions'

export default function createStoreConfig() {
  const state = {
    items: [],
  }
  
  return {
    state,
    actions,
    mutations
  }
}

Step 3

Test instance

await store.dispatch('fetchItems')
expect(store.state.items).toBe(items)
import createStoreConfig from './create-store-config'
import Vuex from 'vuex'
import { fetchData } from '../api'
import { createLocalVue } from '@vue/test-utils'

const localVue = createLocalVue()
localVue.use(Vuex)

jest.mock('../api')

test('returns first 20 items', async () => {
 











})
import createStoreConfig from './create-store-config'
import Vuex from 'vuex'
import { fetchData } from '../api'
import { createLocalVue } from '@vue/test-utils'

const localVue = createLocalVue()
localVue.use(Vuex)

jest.mock('../api')

test('returns first 20 items', async () => {
  const items = Array(22)
    .fill()
    .map((v, i) => ({ i }))
  items[0] = undefined
  fetchData.mockResolvedValue(items)







})
import createStoreConfig from './create-store-config'
import Vuex from 'vuex'
import { fetchData } from '../api'
import { createLocalVue } from '@vue/test-utils'
import { createItems } from './utils'

const localVue = createLocalVue()
localVue.use(Vuex)

jest.mock('../api')

test('returns first 20 items', async () => {
  const items = createItems(22)
  items[0] = undefined
  fetchData.mockResolvedValue(items)

  const storeConfig = createStoreConfig()
  const store = new Vuex.Store(storeConfig)





})
import createStoreConfig from './create-store-config'
import Vuex from 'vuex'
import { fetchData } from '../api'
import { createLocalVue } from '@vue/test-utils'
import { createItems } from './utils'

const localVue = createLocalVue()
localVue.use(Vuex)

jest.mock('../api')

test('returns first 20 items', async () => {
  const items = createItems(22)
  items[0] = undefined
  fetchData.mockResolvedValue(items)

  const storeConfig = createStoreConfig()
  const store = new Vuex.Store(storeConfig)

  await store.dispatch('fetchItems')



})
import createStoreConfig from './create-store-config'
import Vuex from 'vuex'
import { fetchData } from '../api'
import { createLocalVue } from '@vue/test-utils'
import { createItems } from './utils'

const localVue = createLocalVue()
localVue.use(Vuex)

jest.mock('../api')

test('returns first 20 items', async () => {
  const items = createItems(22)
  items[0] = undefined
  fetchData.mockResolvedValue(items)

  const storeConfig = createStoreConfig()
  const store = new Vuex.Store(storeConfig)

  await store.dispatch('fetchItems')

  expect(store.getters.displayItems)
    .toEqual(items.slice(1, 21))
})
import createStoreConfig from './create-store-config'
import Vuex from 'vuex'
import { fetchData } from '../api'
import { createLocalVue } from '@vue/test-utils'
import { createItems } from './utils'

const localVue = createLocalVue()
localVue.use(Vuex)

jest.mock('../api')

test('returns first 20 items', async () => {
  const items = createItems(22)
  items[0] = undefined
  fetchData.mockResolvedValue(items)








})

Testing store instance

  • Less specific
  • Not brittle

Integration test?

npm run test:unit

npm run test:integration

Which approach?

🤷‍♂️

Thank you!

Testing a Vuex store

By Edd Yerburgh

Testing a Vuex store

What, why, and how of unit testing a Vuex store

  • 10,967