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);
});