beakersoft
beakersoft

Reputation: 2356

Testing Vue Components - Mocking State and Methods

I've been trying to get my head around unit testing Vue components, but I can't seem to quite work out how to Mock/Stub out store objects and methods that call an API async.

This is an example of a Vue component we have:

import { mapState, mapGetters } from 'vuex'
import router from 'components/admin/router'

export default {
name: 'Users',
computed: {
    ...mapState('admin', [
        'users',
    ]),
    ...mapGetters({
        requestInProgress: 'requestInProgress',
    }),
},
data: function() {
    return {
        filterTerm: '',
        usersLoaded: false,
    }
},
methods: {        
    getUsers(filter) {
            this.$store.dispatch('admin/getUserList', filter)
                .then(res => {
                    this.usersLoaded = true
                })
                .catch(e => {
                    this.$toast.error({
                        title: 'Failed to retrieve data',
                        message: this.$options.filters.normaliseError(e),
                    })
                })            
    },
},
mounted() {
    this.getUsers('*')
},

}

And this is the test I want to write. I can't even get the test to run cleanly without actually trying to assert anything

import Vue from 'vue'
import { shallowMount } from '@vue/test-utils'
import Users from 'components/admin/pages/user/users.vue'

describe('Users Vue', () => {
    it('Page Should Load', () => {
     const mockResponse = {
          data: [{
            "id": "1",
            "emailAddress": "[email protected]",
            "firstName": "Luke",
            "lastName": "Niland",
            "staffNumber": "12345",
            "phoneNumber": "07707 999999",
            "active": true
        }
    ]};

    let actions
    let store

    beforeEach(() => {
        actions = {
            'admin/getUserList': sinon.stub()                      
                  .returns(Promise.resolve(mockResponse))
        }
        store = new Vuex.Store({
            state: {},
            actions
        })
    })                   

    const wrapper = shallowMount(Users, { store })

    const h5 = wrapper.find('h5')
    expect(h5.text()).toBe('User Administration')  
  })
 })

The errors I tend to get back are about items being undefined, normally, in this case, $store.dispatch is undefined. I feel like I'm missing something with the mocking somewhere, or the fact that the getUsers() being called on the mount is tripping it up.

Upvotes: 5

Views: 5110

Answers (2)

P3trur0
P3trur0

Reputation: 3225

In order to test a Vue component mocking Vuex as you're doing in your example, it is ok to pass a mock store to Vue when you shallowMount your component as you're doing, so:

shallowMount(Users, { store })

but this mock store needs to be mounted also to the base Vue constructor. To do so, you have to pass it to a - localVue. A localVue is a scoped Vue constructor that you can make changes to, in the testing scope, without affecting the global Vue constructor actually used in your application.

Moreover, in your specific case, you did not neither imported nor installed Vuex.

Then, to properly configure your test you need to:

  1. create a localVue instance by invoking the Vue Test Utils utility createLocalVue function and install Vuex on it:
    import { shallowMount, createLocalVue } from '@vue/test-utils'
    import Vuex from 'vuex'

    //creating the local Vue instance for testing
    const localVue = createLocalVue()

    //mounting Vuex to it
    localVue.use(Vuex)
  1. Changing your shallowMount function adding also the localVue instance to the payload:
 const wrapper = shallowMount(Users, { store, localVue })

See here for the Official Documentation references.

Another useful resource about Vue testing is this book (for your specific case, see Chapter 7) and its GitHub repository

Upvotes: 2

Walker Leite
Walker Leite

Reputation: 223

You must create a local Vue to your tests and install Vuex plugin:

import { shallowMount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'

const localVue = createLocalVue()

localVue.use(Vuex)

const wrapper = shallowMount(...)

Upvotes: 1

Related Questions