UNPKG

vuex-mock-store

Version:

Simple and straightforward mock for Vuex v3.x Store

320 lines (238 loc) 8.74 kB
# vuex-mock-store [![Build Status](https://badgen.net/circleci/github/posva/vuex-mock-store)](https://circleci.com/gh/posva/vuex-mock-store) [![npm package](https://badgen.net/npm/v/vuex-mock-store)](https://www.npmjs.com/package/vuex-mock-store) [![coverage](https://badgen.net/codecov/c/github/posva/vuex-mock-store)](https://codecov.io/github/posva/vuex-mock-store) [![thanks](https://img.shields.io/badge/thanks-%E2%99%A5-ff69b4.svg)](https://github.com/posva/thanks) > Simple and straightforward mock for Vuex v3.x and v4.x (Vue 2 and 3) Automatically creates spies on `commit` and `dispatch` so you can focus on testing your component without executing your store code. **Help me keep working on Open Source in a sustainable way 🚀**. Help me with as little as \$1 a month, [sponsor me on Github](https://github.com/sponsors/posva). <h3 align="center">Silver Sponsors</h3> <p align="center"> <a href="https://www.vuemastery.com" title="Vue Mastery" target="_blank"> <img src="https://www.vuemastery.com/images/lgo-vuemastery.svg" alt="Vue Mastery logo" height="48px"> </a> </p> <p align="center"> <a href="https://vuetifyjs.com" target="_blank" title="Vuetify"> <img src="https://vuejs.org/images/vuetify.png" alt="Vuetify logo" height="48px"> </a> </p> <h3 align="center">Bronze Sponsors</h3> <p align="center"> <a href="https://www.storyblok.com" target="_blank" title="Storyblok"> <img src="https://a.storyblok.com/f/51376/3856x824/fea44d52a9/colored-full.png" alt="Storyblok logo" height="32px"> </a> </p> --- ## Installation ```sh npm install -D vuex-mock-store # with yarn yarn add -D vuex-mock-store ``` ## Usage ℹ️: _All examples use [Jest](https://jestjs.io) API_. See [below](#providing-custom-spies) to use a different mock library. Usage with [vue-test-utils](https://github.com/vuejs/vue-test-utils): Given a component `MyComponent.vue`: ```vue <template> <div> <p class="count">{{ count }}</p> <p class="doubleCount">{{ doubleCount }}</p> <button class="increment" @click="increment">+</button> <button class="decrement" @click="decrement">-</button> <hr /> <button class="save" @click="save({ count })">Save</button> </div> </template> <script> import { mapState, mapGetters, mapActions, mapMutations } from 'vuex' export default { computed: { ...mapState(['count']), ...mapGetters(['doubleCount']), }, methods: { ...mapMutations(['increment', 'decrement']), ...mapActions(['save']), }, } </script> ``` You can test interactions without relying on the behaviour of your actions and mutations: ```js import { Store } from 'vuex-mock-store' import { mount } from '@vue/test-utils' import MyComponent from '@/components/MyComponent.vue' // create the Store mock const store = new Store({ state: { count: 0 }, getters: { doubleCount: 0 }, }) // add other mocks here so they are accessible in every component const mocks = { global: { $store: store }, // for Vue 2.x: just { $store: store } without global } // reset spies, initial state and getters afterEach(() => store.reset()) describe('MyComponent.vue', () => { let wrapper beforeEach(() => { wrapper = mount(MyComponent, { mocks }) }) it('calls increment', () => { wrapper.find('button.increment').trigger('click') expect(store.commit).toHaveBeenCalledOnce() expect(store.commit).toHaveBeenCalledWith('increment') }) it('dispatch save with count', () => { wrapper.find('button.save').trigger('click') expect(store.dispatch).toHaveBeenCalledOnce() expect(store.dispatch).toHaveBeenCalledWith('save', { count: 0 }) }) }) ``` ⚠️ The mocked `dispatch` method returns `undefined` instead of a Promise. If you rely on this, you will have to call the appropriate function to make the `dispatch` spy return a Promise: ```js store.dispatch.mockReturnValue(Promise.resolve(42)) ``` If you are using Jest, you can check the documentation [here](https://jestjs.io/docs/en/mock-function-api#mockfnmockreturnvaluevalue) ### Initial state and getters You can provide a `getters`, and `state` object to mock them: ```js const store = new Store({ getters: { name: 'Eduardo', }, state: { counter: 0, }, }) ``` ### Modules #### State To mock module's `state`, provide a nested object in `state` with the same name of the module. As if you were writing the state yourself: ```js new Store({ state: { value: 'from root', moduleA: { value: 'from A', moduleC: { value: 'from A/C', }, }, moduleB: { value: 'from B', }, }, }) ``` That will cover the following calls: ```js import { mapState } from 'vuex' mapState(['value']) // from root mapState('moduleA', ['value']) // from A mapState('moduleB', ['value']) // from B mapState('moduleA/moduleC', ['value']) // from C ``` _When testing `state`, it doesn't change anything for the module to be namespaced or not_ #### Getters To mock module's `getters`, provide the correct name based on whether the module is _namespaced_ or not. Given the following modules: ```js const moduleA = { namespaced: true, getters: { getter: () => 'from A', }, // nested modules modules: { moduleC: { namespaced: true, getter: () => 'from A/C', }, moduleD: { // not namespaced! getter: () => 'from A/D', }, }, } const moduleB = { // not namespaced getters: { getter: () => 'from B', }, } new Vuex.Store({ modules: { moduleA, moduleC } }) ``` We need to use the following getters: ```js new Store({ getters: { getter: 'from root', 'moduleA/getter': 'from A', 'moduleA/moduleC/getter': 'from A/C', 'moduleA/getter': 'from A/D', // moduleD isn't namespaced 'moduleB/getter': 'from B', }, }) ``` #### Actions/Mutations As with _getters_, testing actions and mutations depends whether your [modules are namespaced](https://vuex.vuejs.org/guide/modules.html#namespacing) or not. If they are namespaced, make sure to provide the full action/mutation name: ```js // namespaced module expect(store.commit).toHaveBeenCalledWith('moduleA/setValue') expect(store.dispatch).toHaveBeenCalledWith('moduleA/postValue') // non-namespaced, but could be inside of a module expect(store.commit).toHaveBeenCalledWith('setValue') expect(store.dispatch).toHaveBeenCalledWith('postValue') ``` _Refer to the module example below using `getters` for a more detailed example, even though it is using only `getters`, it's exactly the same for `actions` and `mutations`_ ### Mutating `state`, providing custom `getters` You can [modify](#state) the `state` and `getters` directly for any test. Calling [`store.reset()`](#reset) will reset them to the initial values provided. ## API ### `Store` class #### `constructor(options)` - `options` - `state`: initial state object, _default_: `{}` - `getters`: getters object, _default_: `{}` - `spy`: interface to create spies. [details below](#providing-custom-spies) #### `state` Store state. You can directly modify it to change state: ```js store.state.name = 'Jeff' ``` #### `getters` Store getters. You can directly modify it to change a value: ```js store.getters.upperCaseName = 'JEFF' ``` ℹ️ _Why no functions?_: if you provide a function to a getter, you're reimplementing it. During a test, you know the value, you should be able to provide it directly and be **completely sure** about the value that will be used in the component you are testing. #### `reset` Reset `commit` and `dispatch` spies and restore `getters` and `state` to their initial values #### Providing custom spies By default, the Store will call `jest.fn()` to create the spies. This will throw an error if you are using `mocha` or any other test framework that isn't Jest. In that situation, you will have to provide an interface to _create_ spies. This is the default interface that uses `jest.fn()`: ```js new Store({ spy: { create: (handler) => jest.fn(handler), }, }) ``` The handler is an optional argument that mocks the implementation of the spy. If you use Jest, you don't need to do anything. If you are using something else like [Sinon](https://sinonjs.org), you could provide this interface: ```js import sinon from 'sinon' new Store({ spy: { create: (handler) => sinon.spy(handler), }, }) ``` ### `commit` & `dispatch` Spies. Dependent on the testing framework - [jest.fn](https://jestjs.io/docs/en/jest-object#jestfnimplementation) - [sinon.spy](https://sinonjs.org/releases/v6.3.4/spies) ## Related - [vue-test-utils](https://github.com/vuejs/vue-test-utils) - [vuex](https://github.com/vuejs/vuex) ## License [MIT](http://opensource.org/licenses/MIT)