Mocking Nuxt Global Plugins to Test a Vuex Store File

rdelga80

Ricardo Delgado

Posted on May 6, 2020

Mocking Nuxt Global Plugins to Test a Vuex Store File

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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()
            )
    })
Enter fullscreen mode Exit fullscreen mode

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)
  }
}
Enter fullscreen mode Exit fullscreen mode

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 [{}]
  }
}
Enter fullscreen mode Exit fullscreen mode

And that's all. No more failing tests, and no more undefined plugins.

💖 💪 🙅 🚩
rdelga80
Ricardo Delgado

Posted on May 6, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related