Mastering Vuex: A Step-by-Step Guide to Building and Testing a Vuex Module

What is Vuex?

Vuex is a state management library for Vue applications that allows you to manage state in a centralized manner. It consists of four core concepts:

  • State: The central hub of your application’s data.
  • Getters: Derived state based on the store state.
  • Mutations: The only way to change the state.
  • Actions: Perform asynchronous operations and commit mutations.

Building a Vuex Module

To demonstrate the power of Vuex, let’s create a simple to-do list application. We’ll create a new module for to-do operations, which will be responsible for fetching and updating the state.

Our module will consist of three files:

  • mutation-types.ts: Contains all the function names.
  • actions.ts: Responsible for asynchronous operations.
  • index.ts: The module implementation.
// mutation-types.ts
export enum MutationTypes {
  ON_FETCH_TODOS_SUCCESS = 'ON_FETCH_TODOS_SUCCESS',
  ON_FETCH_TODOS_FAILURE = 'ON_FETCH_TODOS_FAILURE',
}
// actions.ts
import { MutationTypes } from './mutation-types';

export async function fetchTodos({ commit }) {
  try {
    const response = await fetch('https://example.com/api/todos');
    const todos = await response.json();
    commit(MutationTypes.ON_FETCH_TODOS_SUCCESS, todos);
  } catch (error) {
    commit(MutationTypes.ON_FETCH_TODOS_FAILURE, error);
  }
}
// index.ts
import { VuexModule } from 'vuex-module-decorators';
import { MutationTypes } from './mutation-types';
import { fetchTodos } from './actions';

@Module
export default class TodoModule extends VuexModule {
  todos: Todo[] = [];

  get completedTodos() {
    return this.todos.filter(todo => todo.completed);
  }

  @Mutation
  [MutationTypes.ON_FETCH_TODOS_SUCCESS](todos: Todo[]) {
    this.todos = todos;
  }

  @Mutation
  [MutationTypes.ON_FETCH_TODOS_FAILURE](error: Error) {
    console.error(error);
  }

  @Action
  fetchTodos() {
    return fetchTodos(this.context);
  }
}

Initializing Tests

We’ll use Jest as our testing framework. To initialize the tests, we’ll create a store, attach Vuex to Vue, and register the store.

import { createStore } from 'vuex';
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = createStore({
  modules: {
    todo: TodoModule,
  },
});

jest.mock('axios', () => ({
  get: () => Promise.resolve([
    { id: 1, title: 'Task 1', completed: true },
    { id: 2, title: 'Task 2', completed: false },
  ]),
}));

Testing Actions

In our to-do module, we created a fetchTodos action that fetches data from a REST API and fills the state using mutations. We can mock the REST API using a Jest function and validate whether it’s being called and the state is being updated.

it('fetches todos and updates the state', async () => {
  await store.dispatch('todo/fetchTodos');

  expect(store.state.todo.todos).toHaveLength(2);
  expect(store.state.todo.todos[0].completed).toBe(true);
});

Testing Getters

Getter functions simply return the state object. In our example, we have a completedTodos getter that returns the completed to-do items.

it('returns completed todos', () => {
  expect(store.getters['todo/completedTodos']).toHaveLength(1);
});

Testing Mutations

Mutations are the only way to change the state. We can test the ON_FETCH_TODOS_SUCCESS mutation by sending mock to-do tasks and validating whether the state is modified.

it('updates the state on ON_FETCH_TODOS_SUCCESS mutation', () => {
  store.commit('todo/ON_FETCH_TODOS_SUCCESS', [
    { id: 1, title: 'Task 1', completed: true },
    { id: 2, title: 'Task 2', completed: false },
  ]);

  expect(store.state.todo.todos).toHaveLength(2);
});

Leave a Reply