Стабирование встроенных компонентов (Teleport, Suspense)

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


Teleport

Компонент Teleport позволяет рендерить дочерние элементы в другой части DOM вне текущей иерархии компонента. При тестировании с Vue Test Utils важно учитывать, что по умолчанию рендеринг происходит в document.body. Это может вызывать неожиданные результаты при проверке наличия элементов в дереве компонента.

Пример стабирования Teleport

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

const wrapper = mount(MyComponent, {
  global: {
    stubs: {
      Teleport: true
    }
  }
})

Разбор:

  • global.stubs позволяет заменить реальный Teleport на стаб, что предотвращает его перенос элементов в document.body.
  • Внутри теста можно проверять содержимое дочерних элементов без необходимости искать их в DOM вне компонента.
  • Такой подход ускоряет тесты и делает их предсказуемыми.

Альтернативный способ

Если необходимо проверить именно поведение Teleport, можно рендерить его в реальный DOM:

const wrapper = mount(MyComponent, {
  attachTo: document.body
})
  • Использование attachTo позволяет проверить наличие элементов в теле документа.
  • Этот подход необходим для интеграционных тестов, где важно проверить правильность переноса DOM-узлов.

Suspense

Компонент Suspense предназначен для асинхронной загрузки дочерних компонентов. В тестах он может создавать сложности, так как рендеринг задерживается до завершения промисов, переданных через setup или defineAsyncComponent.

Стабирование Suspense

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

const wrapper = mount({
  template: `
    <Suspense>
      <AsyncComponent />
      <template #fallback>
        <div>Loading...</div>
      </template>
    </Suspense>
  `,
  global: {
    stubs: {
      Suspense: true
    }
  }
})

Разбор:

  • Стаб Suspense позволяет игнорировать асинхронность и сразу рендерить дочерние элементы.
  • Фолбэк-шаблон (#fallback) можно тестировать отдельно, если важно проверить состояние ожидания.

Тестирование с реальным Suspense

Для проверки асинхронного рендеринга без стаба:

import { nextTick } from 'vue'

const wrapper = mount({
  template: `
    <Suspense>
      <AsyncComponent />
      <template #fallback>
        <div>Loading...</div>
      </template>
    </Suspense>
  `
})

expect(wrapper.html()).toContain('Loading...') // отображается фолбэк

await nextTick() // ожидаем рендер асинхронного компонента
expect(wrapper.html()).toContain('Async content') // проверяем итоговый рендер

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

  • Использование await nextTick() важно для обработки промисов, которые Suspense ждет перед рендерингом.
  • Такой подход позволяет проверять оба состояния: загрузку и итоговый контент.

Общие рекомендации при тестировании встроенных компонентов

  1. Использование стаба Стабирование встроенных компонентов (Teleport, Suspense) делает тесты быстрее и предотвращает нежелательное взаимодействие с реальным DOM или асинхронными процессами.

  2. Тестирование в реальном DOM Если требуется проверить поведение компонентов в браузере, необходимо использовать attachTo для Teleport и ожидание nextTick или flushPromises для Suspense.

  3. Проверка фолбэков Suspense позволяет определить отдельные шаблоны для состояния загрузки. Тестирование этих шаблонов обеспечивает полное покрытие сценариев рендеринга.

  4. Комбинирование стаба и реального рендера Для комплексных компонентов можно использовать частичное стабирование: стабировать Teleport, но оставить Suspense реальным, либо наоборот, в зависимости от цели теста.


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