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:integrationWhich approach?
🤷♂️
Thank you!
Testing a Vuex store
By Edd Yerburgh
Testing a Vuex store
What, why, and how of unit testing a Vuex store
- 11,606
 
   
   
  