# 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)
