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,905