Моки для состояния

Тестирование компонентов Vue требует управления состоянием приложения, чтобы изолировать поведение компонента и проверить его корректность. Одним из инструментов для этого являются моки состояния — искусственные объекты или функции, заменяющие реальные данные или методы хранилища (store). Использование моков позволяет контролировать возвращаемые значения, проверять вызовы функций и имитировать различные сценарии работы приложения.


Основные подходы к мокам состояния

  1. Моки Vuex Store Vuex является стандартным инструментом управления состоянием в Vue-приложениях. Для тестирования компонентов, использующих store, часто создают мок-версии:
import { shallowMount } from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'

const actions = {
  fetchData: jest.fn()
}

const getters = {
  data: () => [{ id: 1, name: 'Item 1' }]
}

const store = {
  state: {},
  actions,
  getters,
  commit: jest.fn(),
  dispatch: jest.fn()
}

const wrapper = shallowMount(MyComponent, {
  global: {
    mocks: {
      $store: store
    }
  }
})

В этом примере $store заменяется на объект с моками, что позволяет контролировать как данные (getters), так и поведение (actions, commit, dispatch). Такой подход исключает зависимости от реального состояния приложения и делает тесты детерминированными.


  1. Моки локального состояния компонента Иногда необходимо тестировать внутреннее состояние компонента без подключения к Vuex. Для этого можно использовать setData или data при монтировании:
const wrapper = shallowMount(MyComponent, {
  data() {
    return {
      counter: 5
    }
  }
})

expect(wrapper.vm.counter).toBe(5)

wrapper.setData({ counter: 10 })
expect(wrapper.vm.counter).toBe(10)

Моки локального состояния полезны для проверки реактивности и поведения, зависящего от конкретных значений, без необходимости изменения реальных методов компонента.


  1. Моки пропсов Пропсы также можно рассматривать как состояние компонента. Их имитация позволяет тестировать различные сценарии передачи данных:
const wrapper = shallowMount(MyComponent, {
  props: {
    title: 'Test Title',
    isActive: true
  }
})

expect(wrapper.props('title')).toBe('Test Title')
expect(wrapper.props('isActive')).toBe(true)

Пропсы можно изменять через setProps, что позволяет имитировать обновление данных в родительском компоненте:

wrapper.setProps({ isActive: false })
expect(wrapper.props('isActive')).toBe(false)

Взаимодействие моков с методами и вычисляемыми свойствами

Моки состояния напрямую влияют на вычисляемые свойства (computed) и методы компонента. Например:

const wrapper = shallowMount(MyComponent, {
  global: {
    mocks: {
      $store: {
        getters: {
          user: { name: 'Alice', role: 'admin' }
        }
      }
    }
  }
})

expect(wrapper.vm.userName).toBe('Alice')

Здесь userName может быть вычисляемым свойством, возвращающим this.$store.getters.user.name. Мок позволяет протестировать это свойство без реального Vuex.


Проверка вызовов методов через моки

Для имитации действий, вызываемых из состояния, можно использовать функции-заглушки (jest.fn()):

const mockAction = jest.fn()

const store = {
  dispatch: mockAction
}

const wrapper = shallowMount(MyComponent, {
  global: {
    mocks: { $store: store }
  }
})

wrapper.vm.performAction()
expect(mockAction).toHaveBeenCalledWith('someAction', { id: 1 })

Такой подход позволяет не только проверять состояние, но и контролировать взаимодействие компонента с внешним API или Vuex.


Советы по организации моков состояния

  • Изолировать моки для каждого теста — это предотвращает побочные эффекты между тестами.
  • Использовать фабрики моков — функции, возвращающие новый объект состояния для каждого теста.
  • Мокать только необходимое — имитация всего store или всех методов не всегда нужна, достаточно только тех частей, которые влияют на проверяемое поведение.
  • Проверять вызовы функций — это помогает убедиться, что компонент корректно взаимодействует с состоянием, а не просто получает данные.

Примеры сложных сценариев

  1. Имитация изменения состояния через мутации
const mutations = {
  increment: (state) => state.count++
}

const state = { count: 0 }

const store = { state, commit: (mutation) => mutations[mutation](state) }

const wrapper = shallowMount(MyComponent, {
  global: { mocks: { $store: store } }
})

wrapper.vm.incrementCount()
expect(store.state.count).toBe(1)
  1. Комбинация моков пропсов и Vuex
const wrapper = shallowMount(MyComponent, {
  props: { initialValue: 5 },
  global: {
    mocks: {
      $store: {
        getters: { factor: 2 }
      }
    }
  }
})

expect(wrapper.vm.computedValue).toBe(10)

Использование моков для состояния обеспечивает гибкость и предсказуемость тестов компонентов Vue. Они позволяют управлять любыми аспектами данных и методов, влияющих на поведение компонента, создавая среду, независимую от реального хранилища, API или родительских компонентов. Это критически важно для написания надёжных и воспроизводимых тестов.