테스트
Nuxt는 @nuxt/test-utils를 통해 Nuxt 애플리케이션의 엔드 투 엔드 및 단위 테스트를 위한 일류 지원을 제공합니다. 이 라이브러리는 테스트 유틸리티와 설정을 모아둔 것으로, 현재 Nuxt 자체에서 사용하는 테스트와 모듈 생태계 전반의 테스트를 지원합니다.
설치
다른 테스트 의존성을 직접 관리할 수 있도록, @nuxt/test-utils는 다양한 선택적 피어 의존성과 함께 제공됩니다. 예를 들어:
- 런타임 Nuxt 환경으로
happy-dom과jsdom중에서 선택할 수 있습니다. - 엔드 투 엔드 테스트 러너로
vitest,cucumber,jest,playwright중에서 선택할 수 있습니다. - 내장 브라우저 테스트 유틸리티를 사용하려면(그리고 테스트 러너로
@playwright/test를 사용하지 않는 경우)playwright-core만 필요합니다.
npm i --save-dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
yarn add --dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
pnpm add -D @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
bun add --dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
단위 테스트
현재 Nuxt 런타임 환경이 필요한 코드를 위한 단위 테스트 환경을 제공합니다. 현재는 vitest만 지원 하며, 다른 런타임 추가에 대한 기여도 환영합니다.
설정
@nuxt/test-utils/module을nuxt.config파일에 추가합니다(선택 사항). 이 모듈은 Nuxt DevTools에 Vitest 통합을 추가하여 개발 중에 단위 테스트를 실행할 수 있게 해줍니다.export default defineNuxtConfig({ modules: [ '@nuxt/test-utils/module' ] })- 다음과 같은 내용으로
vitest.config.ts파일을 생성합니다:import { defineVitestConfig } from '@nuxt/test-utils/config' export default defineVitestConfig({ // 필요한 Vitest 커스텀 설정 })
@nuxt/test-utils를 import할 때, package.json에 "type": "module"이 지정되어 있거나 vitest 설정 파일의 이름을 적절히 변경해야 합니다.예:
vitest.config.m{ts,js}.
.env.test 파일을 사용하여 테스트용 환경 변수를 설정할 수 있습니다.Nuxt 런타임 환경 사용하기
기본적으로, @nuxt/test-utils는 기본 Vitest 환경을 변경하지 않으므로, 세밀하게 선택적으로 Nuxt 테스트를 다른 단위 테스트와 함께 실행할 수 있습니다.
테스트 파일 이름에 .nuxt.를 추가(예: my-file.nuxt.test.ts 또는 my-file.nuxt.spec.ts)하거나, 테스트 파일에 @vitest-environment nuxt 주석을 직접 추가하여 Nuxt 환경을 사용할 수 있습니다.
// @vitest-environment nuxt
import { test } from 'vitest'
test('my test', () => {
// ... Nuxt 환경에서 테스트!
})
또는 Vitest 설정에서 environment: 'nuxt'를 설정하여 모든 테스트에 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'를 설정한 경우, 필요에 따라 각 테스트 파일에서 기본 환경을 비활성화(opt out) 할 수 있습니다.
// @vitest-environment node
import { test } from 'vitest'
test('my test', () => {
// ... Nuxt 환경 없이 테스트!
})
happy-dom 또는 jsdom 환경에서 실행됩니다. 테스트가 실행되기 전에 글로벌 Nuxt 앱이 초기화됩니다(예: app.vue에 정의한 플러그인이나 코드 실행 포함).즉, 테스트에서 글로벌 상태를 변경하지 않도록 주의해야 하며(또는 필요하다면 이후에 반드시 초기화해야 합니다).🎭 내장 Mock
@nuxt/test-utils는 DOM 환경을 위한 몇 가지 내장 mock을 제공합니다.
intersectionObserver
기본값은 true이며, IntersectionObserver API를 위한 기능 없는 더미 클래스를 생성합니다.
indexedDB
기본값은 false이며, fake-indexeddb를 사용하여 IndexedDB API의 동작하는 mock을 생성합니다.
이들은 vitest.config.ts 파일의 environmentOptions 섹션에서 설정할 수 있습니다:
import { defineVitestConfig } from '@nuxt/test-utils/config'
export default defineVitestConfig({
test: {
environmentOptions: {
nuxt: {
mock: {
intersectionObserver: true,
indexedDb: true,
}
}
}
}
})
🛠️ 헬퍼
@nuxt/test-utils는 Nuxt 앱 테스트를 더 쉽게 해주는 다양한 헬퍼를 제공합니다.
mountSuspended
mountSuspended는 Nuxt 환경 내에서 어떤 Vue 컴포넌트든 마운트할 수 있게 해주며, 비동기 setup과 Nuxt 플러그인에서의 주입(injection)에 접근할 수 있습니다.
mountSuspended는 @vue/test-utils의 mount를 감싸므로, 전달할 수 있는 옵션이나 사용법에 대해서는 Vue Test Utils 문서를 참고하세요.예시:
// 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(
'"This is an auto-imported component"'
)
})
// 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>This is an auto-imported component</div>
<div> I am a global component </div>
<div>/</div>
<a href="/test"> Test link </a>"
`)
})
renderSuspended
renderSuspended는 Nuxt 환경 내에서 @testing-library/vue를 사용하여 어떤 Vue 컴포넌트든 렌더링할 수 있게 해주며, 비동기 setup과 Nuxt 플러그인에서의 주입(injection)에 접근할 수 있습니다.
이 기능은 Testing Library의 유틸리티(예: screen, fireEvent)와 함께 사용해야 합니다. 사용하려면 프로젝트에 @testing-library/vue를 설치하세요.
또한, 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('This is an auto-imported component')).toBeDefined()
})
// 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>This is an auto-imported component</div>
<div> I am a global component </div>
<div>Index page</div><a href="/test"> Test link </a>
</div>"
`)
})
mockNuxtImport
mockNuxtImport를 사용하면 Nuxt의 자동 import 기능을 mock할 수 있습니다. 예를 들어, useStorage를 mock하려면 다음과 같이 할 수 있습니다:
import { mockNuxtImport } from '@nuxt/test-utils/runtime'
mockNuxtImport('useStorage', () => {
return () => {
return { value: 'mocked storage' }
}
})
// 여기에 테스트 작성
mockNuxtImport는 테스트 파일당 mock된 import마다 한 번만 사용할 수 있습니다. 실제로는 vi.mock으로 변환되는 매크로이며, Vitest 문서에서 설명한 대로 vi.mock은 호이스팅됩니다.테스트마다 Nuxt import를 mock하고 다른 구현을 제공해야 한다면, vi.hoisted를 사용해 mock을 생성 및 노출한 뒤, 이를 mockNuxtImport에서 사용할 수 있습니다. 이렇게 하면 mock된 import에 접근할 수 있고, 테스트마다 구현을 변경할 수 있습니다. 테스트 간 mock 상태 변경을 되돌리려면 mock 복원을 각 테스트 전후에 반드시 수행하세요.
import { vi } from 'vitest'
import { mockNuxtImport } from '@nuxt/test-utils/runtime'
const { useStorageMock } = vi.hoisted(() => {
return {
useStorageMock: vi.fn(() => {
return { value: 'mocked storage'}
})
}
})
mockNuxtImport('useStorage', () => {
return useStorageMock
})
// 그런 다음, 테스트 내부에서
useStorageMock.mockImplementation(() => {
return { value: 'something else' }
})
mockComponent
mockComponent를 사용하면 Nuxt의 컴포넌트를 mock할 수 있습니다.
첫 번째 인자는 PascalCase의 컴포넌트 이름이거나, 컴포넌트의 상대 경로입니다.
두 번째 인자는 mock 컴포넌트를 반환하는 팩토리 함수입니다.
예를 들어, MyComponent를 mock하려면 다음과 같이 할 수 있습니다:
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를 사용해 mock 컴포넌트로 리디렉션할 수 있습니다
mockComponent('MyComponent', () => import('./MockComponent.vue'))
// 여기에 테스트 작성
참고: 팩토리 함수 내에서 지역 변수를 참조할 수 없습니다. 팩토리 함수는 호이스팅되기 때문입니다. Vue API나 다른 변수를 사용해야 한다면, 팩토리 함수 내에서 import해야 합니다.
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를 사용하면 mock 데이터를 반환하는 Nitro 엔드포인트를 만들 수 있습니다. API에 요청을 보내 데이터를 표시하는 컴포넌트를 테스트할 때 유용합니다.
첫 번째 인자는 엔드포인트 이름(예: /test/)입니다.
두 번째 인자는 mock 데이터를 반환하는 팩토리 함수입니다.
예를 들어, /test/ 엔드포인트를 mock하려면 다음과 같이 할 수 있습니다:
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 환경 오버라이드 설정 ($test)을 통해 비워두면 모든 요청이 Nitro 서버로 가게 할 수 있습니다.
엔드 투 엔드 테스트와의 충돌
@nuxt/test-utils/runtime과 @nuxt/test-utils/e2e는 서로 다른 테스트 환경에서 실행되어야 하므로 같은 파일에서 사용할 수 없습니다.
@nuxt/test-utils의 엔드 투 엔드 및 단위 테스트 기능을 모두 사용하려면, 테스트를 별도의 파일로 분리하세요. 그런 다음, 파일별로 특별한 // @vitest-environment nuxt 주석을 사용하거나, 런타임 단위 테스트 파일 이름에 .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 사용하기
Nuxt에서 단위 테스트를 위해 @vue/test-utils만 단독으로 사용하고 싶고, Nuxt 컴포저블, 자동 import, context에 의존하지 않는 컴포넌트만 테스트한다면 다음 단계를 따라 설정할 수 있습니다.
- 필요한 의존성 설치
npm i --save-dev vitest @vue/test-utils happy-dom @vitejs/plugin-vueyarn add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vuepnpm add -D vitest @vue/test-utils happy-dom @vitejs/plugin-vuebun add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue - 다음과 같은 내용으로
vitest.config.ts파일을 생성합니다:import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], test: { environment: 'happy-dom', }, }); package.json에 테스트 명령어 추가"scripts": { "build": "nuxt build", "dev": "nuxt dev", ... "test": "vitest" },- 다음과 같은 내용으로 간단한
<HelloWorld>컴포넌트components/HelloWorld.vue생성<template> <p>Hello world</p> </template> - 새로 만든 컴포넌트에 대한 간단한 단위 테스트
~/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('Hello world') }) }) - vitest 명령어 실행
npm run testyarn testpnpm run testbun run test
축하합니다! 이제 Nuxt에서 @vue/test-utils로 단위 테스트를 시작할 준비가 되었습니다. 즐거운 테스트 되세요!
엔드 투 엔드 테스트
엔드 투 엔드 테스트를 위해 Vitest, Jest, Cucumber, Playwright를 테스트 러너로 지원합니다.
설정
@nuxt/test-utils/e2e 헬퍼 메서드를 사용하는 각 describe 블록에서, 시작 전에 테스트 컨텍스트를 설정해야 합니다.
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(browser또는server가 비활성화되었거나,host가 제공된 경우false)
- 타입:
server: 테스트 스위트에서 요청에 응답할 서버를 실행할지 여부.- 타입:
boolean - 기본값:
true(host가 제공된 경우false)
- 타입:
port: 제공된 경우, 실행되는 테스트 서버의 포트를 해당 값으로 설정.- 타입:
number | undefined - 기본값:
undefined
- 타입:
host: 제공된 경우, 새 서버를 빌드 및 실행하는 대신 테스트 대상으로 사용할 URL. 배포된 애플리케이션이나 이미 실행 중인 로컬 서버에 대해 "실제" 엔드 투 엔드 테스트를 실행할 때 유용합니다(테스트 실행 시간을 크게 단축할 수 있음). 아래 대상 호스트 엔드 투 엔드 예제 참고.- 타입:
string - 기본값:
undefined
- 타입:
browser: 내부적으로 Nuxt 테스트 유틸은playwright를 사용해 브라우저 테스트를 수행합니다. 이 옵션이 설정되면 브라우저가 실행되며, 이후 테스트 스위트에서 제어할 수 있습니다.- 타입:
boolean - 기본값:
false
- 타입:
browserOptions- 타입: 다음 속성을 가진
objecttype: 실행할 브라우저 타입 -chromium,firefox,webkit중 하나launch: 브라우저 실행 시 playwright에 전달할 옵션 객체. 전체 API 참조 참고.
- 타입: 다음 속성을 가진
runner: 테스트 스위트의 러너 지정. 현재는 Vitest가 권장됩니다.- 타입:
'vitest' | 'jest' | 'cucumber' - 기본값:
'vitest'
- 타입:
대상 host 엔드 투 엔드 예제
엔드 투 엔드 테스트의 일반적인 사용 사례는, 프로덕션에서 사용하는 것과 동일한 환경에서 실행 중인 배포 애플리케이션에 대해 테스트를 실행하는 것입니다.
로컬 개발 또는 자동 배포 파이프라인에서는 별도의 로컬 서버에 대해 테스트하는 것이 더 효율적이며, 테스트 프레임워크가 테스트마다 다시 빌드하는 것보다 일반적으로 더 빠릅니다.
엔드 투 엔드 테스트를 위해 별도의 대상 호스트를 사용하려면, setup 함수의 host 속성에 원하는 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)
})
})
API
$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'
브라우저에서 테스트하기
@nuxt/test-utils 내에서 Playwright를 사용한 내장 지원을 제공합니다. 프로그래밍 방식 또는 Playwright 테스트 러너를 통해 사용할 수 있습니다.
createPage(url)
vitest, jest, cucumber 내에서, createPage로 설정된 Playwright 브라우저 인스턴스를 만들고(선택적으로) 실행 중인 서버의 경로로 이동할 수 있습니다. 사용 가능한 API 메서드에 대해서는 Playwright 문서를 참고하세요.
import { createPage } from '@nuxt/test-utils/e2e'
const page = await createPage('/page')
// `page` 변수에서 모든 Playwright API에 접근할 수 있습니다
Playwright 테스트 러너로 테스트하기
Playwright 테스트 러너 내에서 Nuxt 테스트를 위한 일류 지원도 제공합니다.
npm i --save-dev @playwright/test @nuxt/test-utils
yarn add --dev @playwright/test @nuxt/test-utils
pnpm add -D @playwright/test @nuxt/test-utils
bun add --dev @playwright/test @nuxt/test-utils
이전 섹션에서 언급한 setup() 함수와 동일한 설정 세부 정보로 글로벌 Nuxt 설정을 제공할 수 있습니다.
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))
}
},
// ...
})
테스트 파일에서는 @nuxt/test-utils/playwright에서 직접 expect와 test를 사용해야 합니다:
import { expect, test } from '@nuxt/test-utils/playwright'
test('test', async ({ page, goto }) => {
await goto('/', { waitUntil: 'hydration' })
await expect(page.getByRole('heading')).toHaveText('Welcome to Playwright!')
})
또는 테스트 파일 내에서 Nuxt 서버를 직접 설정할 수도 있습니다:
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('Welcome to Playwright!')
})