Ricardo Delgado
Posted on May 6, 2020
This is one of those edge cases that drives a developer up the wall, and once it's finally solved you run to The Practical Dev to hopefully spare someone else the pain you went through.
I've written previously about Vue Testing: VueJS Testing: What Not How, and since then have become the "go-to" for my company's Vue testing issues.
But this one was quite a head scratcher.
The Problem
Vue's testing is pretty straight forward thanks to vue-test-utils
. Testing components is really easy, as long as pieces are properly broken down to units (see my post).
With vue-test-utils
you can mount components locally within the test file, test against the local mount, and ta-da tests.
Via the mount, and Jest's functionality, things like plugins and mocks can be handled either locally within the file or globally within config or mock files.
This problem, though, has to deal with Vuex Store files which are not mounted. This is because state, actions, mutations, and getters are tested directly and not within the Vue ecosystem (which behave differently than if tested directly).
Sample Vuex Store file:
export const actions = {
testAction({ commit }, data) {
commit('TEST_MUTATION', data)
}
}
export const mutations = {
TEST_MUTATIONS(state, data) {
state.data = data
}
}
Sample Vuex Store test file:
import { cloneDeep } from 'lodash-es'
import * as testStore from '@/store/testFile'
describe('@/store/testFile', () => {
let actions,
mutations
const cloneStore = cloneDeep(testStore)
beforeEach(() => {
actions = cloneStore.actions
mutations = cloneStore.mutations
)}
it('test action calls test mutation', () => {
const commit = jest.fn()
actions.testActions({ commit })
expect(commit)
.toHaveBeenCalledWith(
'TEST_MUTATION',
expect.anything()
)
})
Approaches
This issue circled around a global plugin called $plugin
, which is a plugin that was created to handle api requests globally.
This means that within the store file there is no imported module, and therefore rules out solutions such as jest.mock()
or adding a file to the __mocks__
directory.
This also ruled out adding to VueTestUtils.config
, since again there is no Vue instance to test against.
Every time the test was run it returned $plugin
as being undefined.
Solution
The solution to this problem is actually pretty easy, and I'm a little surprised it took so long to figure out.
Here's an example of what an action like this may look like:
export const actions = {
async testAction({ commit }) {
let data
try {
data = await this.$plugin.list(`endpoint`)
} catch (e) {
console.log(e)
}
commit('SET_DATA', data)
}
}
When imported into a test file, it acts as a pure function, without having anything to do with Vuex functionality.
That means that this
refers to the actions variable, and not a Vue instance!
Once that was cleared up, I added this to the beforeEach
loop in the test file:
actions.$plugin = {
list: () => {
return [{}]
}
}
And that's all. No more failing tests, and no more undefined plugins.
Posted on May 6, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.