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

Vue Test Utils — это официальная библиотека для тестирования компонентов Vue.js. Она предоставляет удобный API для взаимодействия с компонентами и их состоянием, позволяя проверять корректность работы как отдельных частей компонента, так и его интеграцию с другими частями приложения. Интеграционные тесты позволяют проверить взаимодействие нескольких компонентов и убедиться, что они работают вместе, как ожидается.

Что такое интеграционные тесты?

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

Настройка окружения для интеграционных тестов

Для начала работы с интеграционными тестами с Vue Test Utils необходимо настроить тестовую среду. Обычно для этого используются такие инструменты, как Jest, Mocha или Karma. В качестве примера рассмотрим использование Jest, так как он активно используется в экосистеме Vue.

  1. Установка зависимостей:
npm install --save-dev @vue/test-utils jest vue-jest
  1. Настройка Jest:

В файле package.json нужно добавить конфигурацию для Jest, чтобы использовать vue-jest для трансформации файлов .vue.

"jest": {
  "moduleFileExtensions": [
    "js",
    "json",
    "vue"
  ],
  "transform": {
    "^.+\\.vue$": "vue-jest",
    "^.+\\.js$": "babel-jest"
  }
}

Теперь тесты готовы к запуску с использованием Jest.

Написание интеграционных тестов

Интеграционные тесты проверяют взаимодействие нескольких компонентов в приложении. Рассмотрим пример, когда необходимо протестировать форму для отправки данных пользователя и компонент, который отображает эти данные после отправки.

Предположим, у нас есть два компонента: UserForm.vue и UserDisplay.vue.

  1. UserForm.vue
<template>
  <div>
    <input v-model="name" placeholder="Введите имя" />
    <button @click="submit">Отправить</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: ''
    };
  },
  methods: {
    submit() {
      this.$emit('submit', this.name);
    }
  }
};
</script>
  1. UserDisplay.vue
<template>
  <div>
    <p v-if="name">Привет, {{ name }}!</p>
  </div>
</template>

<script>
export default {
  props: ['name']
};
</script>

Для интеграционного теста нужно проверить, что данные, отправленные из UserForm, корректно отображаются в UserDisplay.

Тестирование интеграции

Для тестирования интеграции этих двух компонентов используем Vue Test Utils для монтирования обоих компонентов в одном тесте. Необходимо убедиться, что:

  • При вводе имени и нажатии на кнопку происходит событие отправки.
  • После отправки данные отображаются в другом компоненте.
import { mount } from '@vue/test-utils';
import UserForm from '@/components/UserForm.vue';
import UserDisplay from '@/components/UserDisplay.vue';

describe('Интеграционные тесты', () => {
  it('должен отобразить имя после отправки формы', async () => {
    const wrapperForm = mount(UserForm);
    const wrapperDisplay = mount(UserDisplay);

    // Мокаем событие submit на компоненте UserForm
    await wrapperForm.setData({ name: 'Иван' });
    await wrapperForm.find('button').trigger('click');

    // Проверяем, что событие было передано
    await wrapperDisplay.setProps({ name: 'Иван' });

    // Проверяем отображение имени в компоненте UserDisplay
    expect(wrapperDisplay.text()).toBe('Привет, Иван!');
  });
});

Объяснение кода теста

  1. mount: Мы монтируем оба компонента (UserForm и UserDisplay) с помощью метода mount из Vue Test Utils. Это позволяет работать с компонентами как с настоящими экземплярами, включая их взаимодействие.
  2. setData: Для имитации ввода в поле формы мы используем метод setData, чтобы установить значение для name.
  3. trigger: С помощью метода trigger мы симулируем клик по кнопке, что вызывает событие отправки данных.
  4. setProps: В компонент UserDisplay передаем новое значение для name через метод setProps, чтобы проверить, что данные обновляются правильно.

Мокирование API-запросов

В реальных приложениях компоненты часто взаимодействуют с внешними сервисами или API. Для интеграционных тестов также необходимо мокировать такие запросы, чтобы изолировать тестируемую логику от реальных внешних сервисов.

Для этого можно использовать библиотеки вроде jest.mock() для подмены функций, осуществляющих запросы. Рассмотрим пример, где компонент выполняет запрос к API для получения данных пользователя.

import { mount } from '@vue/test-utils';
import UserProfile from '@/components/UserProfile.vue';

jest.mock('@/api/user', () => ({
  fetchUser: jest.fn().mockResolvedValue({ name: 'Иван' })
}));

import { fetchUser } from '@/api/user';

describe('UserProfile.vue', () => {
  it('должен отобразить данные пользователя', async () => {
    const wrapper = mount(UserProfile);

    await wrapper.vm.$nextTick(); // ждем завершения асинхронных операций

    expect(fetchUser).toHaveBeenCalled();
    expect(wrapper.text()).toContain('Иван');
  });
});

Здесь мы подменяем метод fetchUser, который обычно делает запрос к серверу, и возвращаем успешный ответ с именем пользователя. Затем проверяем, что данные отобразились на странице.

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

  1. Мокирование зависимостей: Важно изолировать компоненты от внешних зависимостей, таких как API-запросы или глобальное хранилище данных. Используйте моки для подмены этих зависимостей.
  2. Проверка побочных эффектов: Обратите внимание на побочные эффекты, такие как изменения состояния или вызовы методов в родительских компонентах.
  3. Асинхронные операции: Для компонентов, выполняющих асинхронные операции (например, запросы к серверу), используйте await и nextTick для правильной синхронизации с тестами.

Работа с хранилищем данных

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

import { createLocalVue, mount } from '@vue/test-utils';
import Vuex from 'vuex';
import UserProfile from '@/components/UserProfile.vue';

const localVue = createLocalVue();
localVue.use(Vuex);

describe('UserProfile.vue', () => {
  let store;
  let state;

  beforeEach(() => {
    state = { user: { name: 'Иван' } };
    store = new Vuex.Store({
      state
    });
  });

  it('должен отобразить имя из хранилища', () => {
    const wrapper = mount(UserProfile, { store, localVue });
    expect(wrapper.text()).toContain('Иван');
  });
});

В этом примере мы создаем локальное хранилище с помощью createLocalVue и передаем его в компонент через опцию store. Тест проверяет, что компонент корректно отображает данные из хранилища.

Заключение

Интеграционные тесты с Vue Test Utils позволяют проверять взаимодействие компонентов на более высоком уровне, чем юнит-тесты. Они полезны для тестирования сложных сценариев, когда необходимо убедиться, что компоненты корректно взаимодействуют друг с другом и с внешними зависимостями, такими как API или хранилище данных. С помощью правильных техник мока и изоляции можно писать эффективные и устойчивые интеграционные тесты для любых Vue-приложений.