Проверка мутаций (mutations)

Мутации в Vuex — это синхронные функции, изменяющие состояние хранилища. Тестирование мутаций позволяет убедиться, что при вызове конкретной функции состояние изменяется предсказуемо. В отличие от действий (actions), которые могут быть асинхронными и вызывать API, мутации выполняются мгновенно, что делает их идеальными для прямого юнит-тестирования.


Структура мутации

Мутация в Vuex определяется как функция с двумя параметрами: state и payload.

const mutations = {
  increment(state, payload) {
    state.count += payload.amount;
  },
  setUser(state, user) {
    state.user = user;
  }
};
  • state — текущее состояние хранилища.
  • payload — данные, передаваемые мутации для обновления состояния.

Главный принцип тестирования мутации — проверить, что при определенном state и payload состояние изменяется ожидаемым образом.


Базовый тест мутации

Для тестирования мутации используют простой подход: создается локальный объект состояния, вызывается мутация, затем проверяется результат.

import { mutations } from '@/store/mutations';

describe('mutations', () => {
  it('increment увеличивает count на заданное значение', () => {
    const state = { count: 0 };
    const payload = { amount: 5 };

    mutations.increment(state, payload);

    expect(state.count).toBe(5);
  });

  it('setUser обновляет объект пользователя', () => {
    const state = { user: null };
    const user = { id: 1, name: 'Alice' };

    mutations.setUser(state, user);

    expect(state.user).toEqual(user);
  });
});

Ключевые моменты:

  • Тесты изолированы от глобального состояния.
  • Используется локальная копия state, чтобы тесты были детерминированными.
  • Проверка осуществляется через toBe для примитивов и toEqual для объектов.

Тестирование сложных мутаций

Если мутация содержит более сложные операции, например обновление вложенных объектов или массивов, важно проверить все возможные ветвления.

const mutations = {
  addTodo(state, todo) {
    state.todos.push(todo);
  },
  toggleTodo(state, id) {
    const todo = state.todos.find(t => t.id === id);
    if (todo) {
      todo.completed = !todo.completed;
    }
  }
};

describe('todo mutations', () => {
  let state;

  beforeEach(() => {
    state = { todos: [{ id: 1, text: 'Learn Vue', completed: false }] };
  });

  it('addTodo добавляет новый элемент', () => {
    const newTodo = { id: 2, text: 'Write tests', completed: false };
    mutations.addTodo(state, newTodo);
    expect(state.todos.length).toBe(2);
    expect(state.todos[1]).toEqual(newTodo);
  });

  it('toggleTodo меняет completed на противоположное значение', () => {
    mutations.toggleTodo(state, 1);
    expect(state.todos[0].completed).toBe(true);
    mutations.toggleTodo(state, 1);
    expect(state.todos[0].completed).toBe(false);
  });
});

Особенности:

  • Использование beforeEach гарантирует чистое состояние для каждого теста.
  • Проверяются как изменение существующих данных, так и добавление новых.

Проверка побочных эффектов

Мутации должны быть синхронными и не вызывать асинхронные операции или прямой доступ к API. Если мутация изменяет несколько полей состояния одновременно, важно тестировать все изменения:

const mutations = {
  updateProfile(state, profile) {
    state.user.name = profile.name;
    state.user.email = profile.email;
  }
};

describe('updateProfile', () => {
  it('обновляет имя и email пользователя', () => {
    const state = { user: { name: 'Bob', email: 'bob@example.com' } };
    const profile = { name: 'Alice', email: 'alice@example.com' };

    mutations.updateProfile(state, profile);

    expect(state.user.name).toBe('Alice');
    expect(state.user.email).toBe('alice@example.com');
  });
});

Рекомендации по тестированию мутаций

  • Использовать минимальный и локальный state для каждого теста.
  • Проверять все ветвления и условия внутри мутации.
  • Проверять обновление объектов и массивов через глубокое сравнение (toEqual).
  • Не включать асинхронные операции — они должны быть в действиях (actions).
  • Организовывать тесты по смыслу, например, группировать мутации по модулю или функционалу.

Интеграция с Vue Test Utils

Хотя Vue Test Utils ориентирован на тестирование компонентов, мутации часто проверяются отдельно. В интеграционных тестах компонентов можно проверять, что событие компонента вызывает нужную мутацию через моки хранилища:

import { shallowMount } from '@vue/test-utils';
import Counter from '@/components/Counter.vue';

const mutations = {
  increment: jest.fn()
};

const store = {
  state: { count: 0 },
  mutations
};

describe('Counter.vue', () => {
  it('вызывает increment при клике', async () => {
    const wrapper = shallowMount(Counter, {
      global: { mocks: { $store: store } }
    });

    await wrapper.find('button').trigger('click');

    expect(mutations.increment).toHaveBeenCalled();
  });
});

Особенности интеграции:

  • Используются моки (jest.fn) для отслеживания вызова мутации.
  • Не проверяется реальное изменение состояния — только факт вызова.
  • Подход позволяет отделить логику компонента от состояния хранилища.

Проверка мутаций является фундаментальной частью тестирования Vuex. Изоляция, детерминированность и тщательная проверка всех изменений состояния обеспечивают надежность и предсказуемость приложения при масштабировании.