Обработка различных HTTP статусов

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


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

Наиболее распространённый способ — использование сторонних библиотек для мокирования, таких как axios-mock-adapter для Axios или fetch-mock для fetch. Это позволяет подменять реальные сетевые вызовы фиктивными данными и статусами.

Пример с Axios и Jest:

import { mount } from '@vue/test-utils';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import MyComponent from '@/components/MyComponent.vue';

describe('MyComponent', () => {
  let mock;

  beforeEach(() => {
    mock = new MockAdapter(axios);
  });

  afterEach(() => {
    mock.restore();
  });

  it('обрабатывает статус 200', async () => {
    mock.onGet('/api/data').reply(200, { message: 'OK' });

    const wrapper = mount(MyComponent);
    await wrapper.vm.fetchData();

    expect(wrapper.text()).toContain('OK');
  });

  it('обрабатывает статус 404', async () => {
    mock.onGet('/api/data').reply(404);

    const wrapper = mount(MyComponent);
    await wrapper.vm.fetchData();

    expect(wrapper.text()).toContain('Ошибка: 404');
  });

  it('обрабатывает статус 500', async () => {
    mock.onGet('/api/data').reply(500);

    const wrapper = mount(MyComponent);
    await wrapper.vm.fetchData();

    expect(wrapper.text()).toContain('Ошибка сервера');
  });
});

Ключевой момент — использование метода reply(status, data) для задания конкретного HTTP-кода и ответа. Это позволяет проверить, как компонент реагирует на успешные и ошибочные ответы.


Проверка реакции на различные статусы

Компонент, работающий с API, обычно реализует одну из следующих стратегий:

  • Успешный ответ (2xx) — отображение данных.
  • Клиентская ошибка (4xx) — вывод сообщения об ошибке пользователя.
  • Серверная ошибка (5xx) — уведомление о проблемах на сервере или повторная попытка.

Пример Vue-компонента:

<template>
  <div>
    <p v-if="error">{{ error }}</p>
    <p v-else-if="data">{{ data.message }}</p>
    <p v-else>Загрузка...</p>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      data: null,
      error: null,
    };
  },
  methods: {
    async fetchData() {
      try {
        const response = await axios.get('/api/data');
        this.data = response.data;
        this.error = null;
      } catch (e) {
        if (e.response) {
          if (e.response.status >= 500) {
            this.error = 'Ошибка сервера';
          } else if (e.response.status >= 400) {
            this.error = `Ошибка: ${e.response.status}`;
          }
        } else {
          this.error = 'Неизвестная ошибка';
        }
      }
    },
  },
};
</script>

Важно тестировать не только данные, но и состояние компонента после ошибок. Vue Test Utils позволяет проверять DOM после асинхронных операций, используя await nextTick() или await wrapper.vm.$nextTick().


Асинхронные операции и nextTick

Асинхронные запросы требуют ожидания обновления DOM перед проверкой:

import { nextTick } from 'vue';

it('отображает сообщение об ошибке после 404', async () => {
  mock.onGet('/api/data').reply(404);

  const wrapper = mount(MyComponent);
  await wrapper.vm.fetchData();
  await nextTick();

  expect(wrapper.text()).toContain('Ошибка: 404');
});

nextTick() гарантирует, что Vue завершил все реактивные обновления, прежде чем проверять состояние компонента.


Параметризация тестов для различных статусов

Чтобы избежать дублирования кода, можно использовать test.each или аналогичные конструкции для проверки множества статусов:

test.each([
  [200, 'OK', null],
  [404, null, 'Ошибка: 404'],
  [500, null, 'Ошибка сервера'],
])('статус %i обрабатывается корректно', async (status, message, error) => {
  mock.onGet('/api/data').reply(status, { message });

  const wrapper = mount(MyComponent);
  await wrapper.vm.fetchData();
  await nextTick();

  if (message) {
    expect(wrapper.text()).toContain(message);
  }
  if (error) {
    expect(wrapper.text()).toContain(error);
  }
});

Такой подход повышает читаемость тестов и упрощает добавление новых случаев.


Совмещение с Jest spies

Для проверки вызовов методов и логики обработки можно использовать jest.spyOn:

it('вызывает метод fetchData при создании', async () => {
  const spy = jest.spyOn(MyComponent.methods, 'fetchData');
  mount(MyComponent);
  expect(spy).toHaveBeenCalled();
  spy.mockRestore();
});

Это позволяет удостовериться, что компонент инициирует запросы именно в нужный момент жизненного цикла.


Обработка нестандартных статусов

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

  • Поведение компонента при отсутствии response в ошибке.
  • Обработку неожиданных полей в ответе.
  • Сценарии таймаута или сетевых сбоев.

Пример обработки таймаута:

mock.onGet('/api/data').timeout();

const wrapper = mount(MyComponent);
await wrapper.vm.fetchData();
await nextTick();

expect(wrapper.text()).toContain('Неизвестная ошибка');

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