Nuxt mid-level certification launch date announced!

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

Как протестировать ваше приложение Nuxt.
Если вы являетесь автором модуля, более подробную информацию вы можете найти в Руководстве автора модуля.

Nuxt предлагает первоклассную поддержку для сквозного (end-to-end) и юнит тестирования приложения Nuxt с помощью @nuxt/test-utils, библиотеки тестовых утилит и конфигурации, которая в настоящее время поддерживает тесты, которые мы используем в самом Nuxt и тесты по всей экосистеме модулей.

Посмотрите видео от Александра Лихтера о начале работы с @nuxt/test-utils.

Установка

Чтобы вы могли управлять другими зависимостями тестирования, @nuxt/test-utils поставляется с различными необязательными равнозначными зависимостями. Например:

  • вы можете выбрать между happy-dom и jsdom для runtime Nuxt
  • вы можете выбрать между vitest, cucumber, jest и playwright для сквозных тестов
  • playwright-core требуется только в том случае, если вы хотите использовать встроенные в браузер утилиты тестирования (и не используете @playwright/test в качестве средства запуска тестов)
npm i --save-dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core

Юнит-тестирование

Мы поставляем среду для кода модульного тестирования, которому нужна среда выполнения Nuxt. В настоящее время она поддерживает только vitest (хотя вклад в добавление других сред выполнения будет приветствоваться).

Настройка

  1. Добавьте @nuxt/test-utils/module в файл nuxt.config (опционально). Это добавляет интеграцию Vitest в Nuxt DevTools, которая поддерживает запуск ваших модульных тестов в процессе разработки.
    export default 
    defineNuxtConfig
    ({
    modules
    : [
    '@nuxt/test-utils/module' ] })
  2. Создайте vitest.config.ts со следующим содержимым:
    import { 
    defineVitestConfig
    } from '@nuxt/test-utils/config'
    export default
    defineVitestConfig
    ({
    // любая необходимая пользовательская конфигурация Vitest })
При импорте @nuxt/test-utils в конфигурацию vitest, необходимо указать "type": "module" в package.json или переименовать файл конфигурации vitest соответствующим образом.

Т.е. vitest.config.m{ts,js}.

Использование среды выполнения Nuxt

По умолчанию, @nuxt/test-utils не изменяет окружение Vitest, поэтому вы можете выполнить детальную настройку и запустить тесты Nuxt вместе с другими юнит-тестами.

Вы можете включить окружение Nuxt, добавив .nuxt. к имени тестового файла (например, my-file.nuxt.test.ts или my-file.nuxt.spec.ts) или добавив @vitest-environment nuxt в качестве комментария непосредственно в тестовом файле.

// @vitest-environment nuxt
import { 
test
} from 'vitest'
test
('my test', () => {
// ... тест в среде выполнения Nuxt! })

В качестве альтернативы вы можете установить environment: 'nuxt' в конфигурации Vitest, чтобы включить окружение Nuxt для всех тестов.

// vitest.config.ts
import { 
fileURLToPath
} from 'node:url'
import {
defineVitestConfig
} from '@nuxt/test-utils/config'
export default
defineVitestConfig
({
test
: {
environment
: 'nuxt',
// вы можете по желанию задать параметры окружения, специфичные для Nuxt // environmentOptions: { // nuxt: { // rootDir: fileURLToPath(new URL('./playground', import.meta.url)), // domEnvironment: 'happy-dom', // 'happy-dom' (по умолчанию) или 'jsdom' // overrides: { // // другие настройки Nuxt, которые вы хотите передать // } // } // } } })

Если вы установили environment: 'nuxt' по умолчанию, вы можете отказаться от окружения по умолчанию для каждого тестового файла по мере необходимости.

// @vitest-environment node
import { 
test
} from 'vitest'
test
('my test', () => {
// ... тест без окружения Nuxt! })
Когда вы запускаете тесты в окружении Nuxt, они будут запущены в среде happy-dom или jsdom. Перед запуском ваших тестов будет инициализировано глобальное приложение Nuxt (включая, например, запуск любых плагинов или кода, которые вы определили в своем app.vue).Это означает, что вам следует проявлять особую осторожность, чтобы не изменять глобальное состояние в своих тестах (или, если это необходимо, сбрасывать его впоследствии).

🎭 Встроенные моки

@nuxt/test-utils предоставляет несколько встроенных моков для среды DOM.

intersectionObserver

Значение по умолчанию - true, создает фиктивный класс без какой-либо функциональности для IntersectionObserver API

indexedDB

Значение по умолчанию - false, используется fake-indexeddb для создания функциональной имитации API IndexedDB.

Их можно настроить в разделе environmentOptions файла vitest.config.ts:

import { 
defineVitestConfig
} from '@nuxt/test-utils/config'
export default
defineVitestConfig
({
test
: {
environmentOptions
: {
nuxt
: {
mock
: {
intersectionObserver
: true,
indexedDb
: true,
} } } } })

🛠️ Хэлперы

@nuxt/test-utils предоставляет ряд вспомогательных средств для упрощения тестирования приложений Nuxt.

mountSuspended

mountSuspended позволяет монтировать любой компонент Vue в окружение Nuxt, позволяя асинхронную настройку и доступ к инъекциям из плагинов Nuxt.

Under the hood, mountSuspended wraps mount from @vue/test-utils, so you can check out the Vue Test Utils documentation for more on the options you can pass, and how to use this utility.

Например:

// tests/components/SomeComponents.nuxt.spec.ts
import { 
mountSuspended
} from '@nuxt/test-utils/runtime'
import {
SomeComponent
} from '#components'
it
('can mount some component', async () => {
const
component
= await
mountSuspended
(
SomeComponent
)
expect
(
component
.
text
()).
toMatchInlineSnapshot
(
'"Это автоматически импортируемый компонент."' ) })
import { it, expect } from 'vitest'
// ---cut---
// tests/components/SomeComponents.nuxt.spec.ts
import { mountSuspended } from '@nuxt/test-utils/runtime'
import App from '~/app.vue'

// tests/App.nuxt.spec.ts
it('can also mount an app', async () => {
    const component = await mountSuspended(App, { route: '/test' })
    expect(component.html()).toMatchInlineSnapshot(`
      "<div>Это автоматически импортируемый компонент</div>
      <div> Я глобальный компонент </div>
      <div>/</div>
      <a href="/test"> Тестовая ссылка </a>"
    `)
})

renderSuspended

renderSuspended позволяет отрисовать любой компонент Vue в окружении Nuxt с помощью @testing-library/vue, обеспечивая асинхронную настройку и доступ к инъекциям из плагинов Nuxt.

This should be used together with utilities from Testing Library, e.g. screen and fireEvent. Install @testing-library/vue in your project to use these.

Кроме того, Testing Library также полагается на глобальные переменные тестирования для очистки. Вам следует включить их в конфигурации Vitest.

Переданный компонент будет отображен внутри <div id="test-wrapper"></div>.

Примеры:

// tests/components/SomeComponents.nuxt.spec.ts
import { 
renderSuspended
} from '@nuxt/test-utils/runtime'
import {
SomeComponent
} from '#components'
import {
screen
} from '@testing-library/vue'
it
('can render some component', async () => {
await
renderSuspended
(
SomeComponent
)
expect
(
screen
.
getByText
('Это автоматически импортируемый компонент')).
toBeDefined
()
})
import { it, expect } from 'vitest'
// ---cut---
// tests/App.nuxt.spec.ts
import { renderSuspended } from '@nuxt/test-utils/runtime'
import App from '~/app.vue'

it('can also render an app', async () => {
  const html = await renderSuspended(App, { route: '/test' })
  expect(html).toMatchInlineSnapshot(`
    "<div id="test-wrapper">
      <div>Это автоматически импортируемый компонент</div>
      <div> Я глобальный компонент </div>
      <div>Главная страница</div><a href="/test"> Тестовая ссылка </a>
    </div>"
  `)
})

mockNuxtImport

mockNuxtImport позволяет вам имитировать функцию автоматического импорта Nuxt. Например, чтобы имитировать useStorage, вы можете сделать это следующее:

import { 
mockNuxtImport
} from '@nuxt/test-utils/runtime'
mockNuxtImport
('useStorage', () => {
return () => { return {
value
: 'mocked storage' }
} }) // здесь ваши тесты
mockNuxtImport можно использовать для мока импорта только один раз на тестовый файл. На самом деле это макрос, который преобразуется в vi.mock, а vi.mock поднимается, как описано здесь.

Если вам нужно имитировать импорт Nuxt и предоставлять разные реализации между тестами, вы можете сделать это, создав и предоставив свои моки с помощью vi.hoisted, а затем использовать эти моки в mockNuxtImport. После этого, вы получаете доступ к мокам импортов и можете изменять реализацию между тестами. Будьте осторожны, восстанавливая моки до или после каждого теста, чтобы отменить изменения состояния мока между запусками.

import { 
vi
} from 'vitest'
import {
mockNuxtImport
} from '@nuxt/test-utils/runtime'
const {
useStorageMock
} =
vi
.
hoisted
(() => {
return {
useStorageMock
:
vi
.
fn
().
mockImplementation
(() => {
return {
value
: 'mocked storage'}
}) } })
mockNuxtImport
('useStorage', () => {
return useStorageMock }) // Затем, внутри теста
useStorageMock
.
mockImplementation
(() => {
return {
value
: 'something else' }
})

mockComponent

mockComponent позволяет вам имитировать компонент Nuxt. Первый аргумент может быть именем компонента в PascalCase или относительным путем к нему. Второй аргумент — это фабричная функция, которая возвращает фиктивный компонент.

Например, чтобы создать мок MyComponent, вы можете:

import { mockComponent } from '@nuxt/test-utils/runtime'

mockComponent('MyComponent', {
  props: {
    value: String
  },
  setup(props) {
    // ...
  }
})

// относительный путь или псевдоним также работают
mockComponent('~/components/my-component.vue', async () => {
  // или фабричная функция
  return defineComponent({
    setup(props) {
      // ...
    }
  })
})

// или вы можете использовать SFC для перенаправления на мок компонента
mockComponent('MyComponent', () => import('./MockComponent.vue'))

// здесь ваши тесты

Примечание: Вы не можете ссылаться на локальные переменные в фабричной функции, потому что они поднимаются (hoisted). Если вам нужно получить доступ к API Vue или другим переменным, вам нужно импортировать их в фабричную функцию.

import { 
mockComponent
} from '@nuxt/test-utils/runtime'
mockComponent
('MyComponent', async () => {
const {
ref
,
h
} = await import('vue')
return
defineComponent
({
setup
(
props
) {
const
counter
=
ref
(0)
return () =>
h
('div', null,
counter
.
value
)
} }) })

registerEndpoint

registerEndpoint позволяет вам создать эндпоинт Nitro, который возвращает имитированные данные. Это может пригодиться, если вы хотите протестировать компонент, который делает запросы к API для отображения некоторых данных.

Первый аргумент — это имя эндпоинта (например, /test/). Второй аргумент — это функция-фабрика, которая возвращает имитированные данные.

Например, чтобы имитировать эндпоинт /test/, вы можете сделать следующее:

import { 
registerEndpoint
} from '@nuxt/test-utils/runtime'
registerEndpoint
('/test/', () => ({
test
: 'test-field'
}))

По умолчанию, запрос будет сделан с использованием метода GET. Вы можете использовать другой метод, установив объект в качестве второго аргумента вместо функции.

import { 
registerEndpoint
} from '@nuxt/test-utils/runtime'
registerEndpoint
('/test/', {
method
: 'POST',
handler
: () => ({
test
: 'test-field' })
})

Примечание: Если ваши запросы в компоненте направляются к внешнему API, вы можете использовать baseURL, а затем сделать его пустым с помощью Nuxt Environment Override Config ($test), чтобы все ваши запросы направлялись на сервер Nitro.

Конфликт со сквозным тестированием

@nuxt/test-utils/runtime и @nuxt/test-utils/e2e должны запускаться в разных тестовых средах, поэтому их нельзя использовать в одном файле.

Если вы хотите использовать как сквозную, так и модульную функциональность тестирования @nuxt/test-utils, вы можете разделить свои тесты на отдельные файлы. Затем вы либо указываете тестовую среду для каждого файла с помощью специального комментария // @vitest-environment nuxt, либо называете файлы модульных runtime тестов с расширением .nuxt.spec.ts.

app.nuxt.spec.ts

import { 
mockNuxtImport
} from '@nuxt/test-utils/runtime'
mockNuxtImport
('useStorage', () => {
return () => { return {
value
: 'mocked storage' }
} })

app.e2e.spec.ts

import { 
setup
,
$fetch
} from '@nuxt/test-utils/e2e'
await
setup
({
setupTimeout
: 10000,
}) // ...

Использование @vue/test-utils

Если вы предпочитаете использовать @vue/test-utils отдельно для юнит-тестирования в Nuxt и тестируете только компоненты, которые не полагаются на композаблы Nuxt, автоматический импорт или контекст, вы можете выполнить следующие шаги для его настройки.

  1. Установите необходимые зависимости
    npm i --save-dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue
    
  2. Создайте vitest.config.ts со следующим содержимым:
    import { 
    defineConfig
    } from 'vitest/config'
    import
    vue
    from '@vitejs/plugin-vue'
    export default
    defineConfig
    ({
    plugins
    : [
    vue
    ()],
    test
    : {
    environment
    : 'happy-dom',
    }, });
  3. Добавьте новую команду для теста в ваш package.json
    "scripts": {
      "build": "nuxt build",
      "dev": "nuxt dev",
      ...
      "test": "vitest"
    },
    
  4. Создайте простой компонент <HelloWorld> components/HelloWorld.vue со следующим содержимым:
    <template>
      <p>Привет, мир!</p>
    </template>
    
  5. Создайте простой модульный тест для этого компонента. ~/components/HelloWorld.spec.ts
    import { describe, it, expect } from 'vitest'
    import { mount } from '@vue/test-utils'
    
    import HelloWorld from './HelloWorld.vue'
    
    describe('HelloWorld', () => {
      it('component renders Hello world properly', () => {
        const wrapper = mount(HelloWorld)
        expect(wrapper.text()).toContain('Привет, мир!')
      })
    })
    
  6. Запустить команду vitest
    npm run test
    

Поздравляем, вы готовы начать модульное тестирование с @vue/test-utils в Nuxt! Удачного тестирования!

Сквозное (End-To-End) Тестирование

Для сквозного тестирования мы поддерживаем в качестве тест-раннеров: Vitest, Jest, Cucumber и Playwright.

Настройка

В каждом блоке describe, где вы используете вспомогательные методы @nuxt/test-utils/e2e, вам необходимо настроить тестовый контекст перед началом.

test/my-test.spec.ts
import { 
describe
,
test
} from 'vitest'
import {
setup
,
$fetch
} from '@nuxt/test-utils/e2e'
describe
('My test', async () => {
await
setup
({
// параметры тестового контекста })
test
('my test', () => {
// ... }) })

"За кулисами" setup выполняет ряд задач в beforeAll, beforeEach, afterEach и afterAll для правильной настройки тестовой среды Nuxt.

Пожалуйста, используйте приведенные ниже параметры для метода setup.

Конфигурация Nuxt

  • rootDir: Путь к каталогу с приложением Nuxt, которое будет протестировано.
    • Тип: string
    • По умолчанию: '.'
  • configFile: Имя файла конфигурации.
    • Тип: string
    • По умолчанию: 'nuxt.config'

Тайминги

  • setupTimeout: Количество времени (в миллисекундах), отведенное setupTest на завершение своей работы (которая может включать в себя сборку или генерацию файлов для приложения Nuxt, в зависимости от переданных параметров).
    • Тип: number
    • По умолчанию: 60000

Возможности

  • build: Следует ли запускать отдельный этап сборки.
    • Тип: boolean
    • По умолчанию: true (false, если browser или server отключены, или если указан host)
  • server: Следует ли запускать сервер для ответа на запросы в наборе тестов.
    • Тип: boolean
    • По умолчанию: true (false, если указан host)
  • port: If provided, set the launched test server port to the value.
    • Type: number | undefined
    • Default: undefined
  • host: Если указан, URL-адрес для использования в качестве цели тестирования вместо создания и запуска нового сервера. Полезно для выполнения "реальных" сквозных тестов на развернутой версии вашего приложения или на уже запущенном локальном сервере (что может значительно сократить время выполнения теста). См. пример сквозного теста целевого хоста ниже.
    • Тип: string
    • По умолчанию: undefined
  • browser: Под капотом для проведения тестирования в браузере Nuxt test utils использует playwright. Если эта опция установлена, браузер будет запущен и им можно будет управлять в последующем наборе тестов.
    • Тип: boolean
    • По умолчанию: false
  • browserOptions
    • Тип: object со следующими свойствами
      • type: Тип запускаемого браузера - Chromium, Firefox или WebKit
      • launch: object опций, которые будут переданы playwright при запуске браузера. См. полный справочник API.
  • runner: Укажите раннера для набора тестов. В настоящее время рекомендуется Vitest.
    • Тип: 'vitest' | 'jest' | 'cucumber'
    • По умолчанию: 'vitest'
Пример сквозного теста целевого host

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

Для локальной разработки или автоматизированных пайплайнов деплоя, тестирование на отдельном локальном сервере может быть более эффективным и обычно быстрее, чем ребилд тестовой среды между тестами.

Чтобы использовать отдельный целевой хост для сквозных тестов, просто укажите свойство host функции setup с нужным URL-адресом.

import { setup, createPage } from '@nuxt/test-utils/e2e'
import { describe, it, expect } from 'vitest'

describe('login page', async () => {
  await setup({
    host: 'http://localhost:8787',
  })

  it('displays the email and password fields', async () => {
    const page = await createPage('/login')
    expect(await page.getByTestId('email').isVisible()).toBe(true)
    expect(await page.getByTestId('password').isVisible()).toBe(true)
  })
})
Target host end-to-end example

A common use-case for end-to-end testing is running the tests against a deployed application running in the same environment typically used for Production.

For local development or automated deploy pipelines, testing against a separate local server can be more efficient and is typically faster than allowing the test framework to rebuild between tests.

To utilize a separate target host for end-to-end tests, simply provide the host property of the setup function with the desired URL.

import { 
setup
,
createPage
} from '@nuxt/test-utils/e2e'
import {
describe
,
it
,
expect
} from 'vitest'
describe
('login page', async () => {
await
setup
({
host
: 'http://localhost:8787',
})
it
('displays the email and password fields', async () => {
const
page
= await
createPage
('/login')
expect
(await
page
.
getByTestId
('email').
isVisible
()).
toBe
(true)
expect
(await
page
.
getByTestId
('password').
isVisible
()).
toBe
(true)
}) })

APIs

$fetch(url)

Получает HTML-код страницы, обработанной сервером.

import { 
$fetch
} from '@nuxt/test-utils/e2e'
const
html
= await
$fetch
('/')

fetch(url)

Получает ответ страницы, отрендеренной сервером.

import { 
fetch
} from '@nuxt/test-utils/e2e'
const
res
= await
fetch
('/')
const {
body
,
headers
} = res

url(path)

Получает полный URL-адрес заданной страницы (включая порт, на котором работает тестовый сервер).

import { 
url
} from '@nuxt/test-utils/e2e'
const
pageUrl
=
url
('/page')
// 'http://localhost:6840/page'

Тестирование в браузере

Мы предоставляем встроенную поддержку использования Playwright в @nuxt/test-utils, как программно, так и через средство запуска тестов Playwright.

createPage(url)

В vitest, jest или cucumber вы можете создать настроенный экземпляр браузера в Playwright с помощью createPage и (опционально) указать ему путь от работающего сервера. Вы можете узнать больше о доступных методах API из документации Playwright.

import { 
createPage
} from '@nuxt/test-utils/e2e'
const
page
= await
createPage
('/page')
// вы можете получить доступ ко всему API Playwright из переменной `page`

Тестирование с помощью тест-раннера Playwright

Мы также предоставляем первоклассную поддержку для тестирования Nuxt в тест-раннере Playwright.

npm i --save-dev @playwright/test @nuxt/test-utils

Вы можете предоставить глобальную конфигурацию Nuxt с теми же параметрами, что и в функции setup(), упомянутой ранее в этом разделе.

playwright.config.ts
import { fileURLToPath } from 'node:url'
import { defineConfig, devices } from '@playwright/test'
import type { ConfigOptions } from '@nuxt/test-utils/playwright'

export default defineConfig<ConfigOptions>({
  use: {
    nuxt: {
      rootDir: fileURLToPath(new URL('.', import.meta.url))
    }
  },
  // ...
})
Узнать больше Посмотреть полный пример конфигурации.

Ваш тестовый файл должен использовать expect и test непосредственно из @nuxt/test-utils/playwright:

tests/example.test.ts
import { expect, test } from '@nuxt/test-utils/playwright'

test('test', async ({ page, goto }) => {
  await goto('/', { waitUntil: 'hydration' })
  await expect(page.getByRole('heading')).toHaveText('Добро пожаловать в Playwright!')
})

Вы также можете настроить сервер Nuxt непосредственно в тестовом файле:

tests/example.test.ts
import { expect, test } from '@nuxt/test-utils/playwright'

test.use({
  nuxt: {
    rootDir: fileURLToPath(new URL('..', import.meta.url))
  }
})

test('test', async ({ page, goto }) => {
  await goto('/', { waitUntil: 'hydration' })
  await expect(page.getByRole('heading')).toHaveText('Добро пожаловать в Playwright!')
})