浏览器模式 实验性
本页面介绍了 Vitest API 中的实验性浏览器模式功能。此功能允许您在浏览器环境中原生运行测试,从而能够访问 window 和 document 等浏览器全局对象。该功能目前仍在开发中,未来的 API 可能会有所变化。
TIP
如果您正在寻找 expect、vi 或任何通用 API(如测试项目或类型测试)的文档,请参阅《入门指南》。


安装
为了简化设置,您可以使用 vitest init browser 命令安装所需的依赖项并创建浏览器配置。
npx vitest init browseryarn exec vitest init browserpnpx vitest init browserbunx vitest init browser手动安装
您也可以手动安装这些包。默认情况下,浏览器模式在本地运行测试时不需要任何额外的端到端(E2E)提供者,因为它会复用您现有的浏览器。
npm install -D vitest @vitest/browseryarn add -D vitest @vitest/browserpnpm add -D vitest @vitest/browserbun add -D vitest @vitest/browserWARNING
然而,要在持续集成(CI)环境中运行测试,您需要安装 playwright 或 webdriverio。我们还建议在本地测试时切换到其中一个提供者,而不是使用默认的 preview 提供者,因为 preview 依赖于模拟事件,而 Playwright/WebdriverIO 使用 Chrome DevTools Protocol。
如果您尚未接触这些工具,建议从 Playwright 开始,因为它支持并行执行,这能让您的测试运行得更快。此外,Playwright 使用 Chrome DevTools Protocol,通常比 WebDriver 更快。
::: tabs key:provider == Playwright Playwright 是一个用于 Web 测试和自动化的框架。
npm install -D vitest @vitest/browser playwrightyarn add -D vitest @vitest/browser playwrightpnpm add -D vitest @vitest/browser playwrightbun add -D vitest @vitest/browser playwright== WebdriverIO
WebdriverIO 允许您使用 WebDriver 协议在本地运行测试。
npm install -D vitest @vitest/browser webdriverioyarn add -D vitest @vitest/browser webdriveriopnpm add -D vitest @vitest/browser webdriveriobun add -D vitest @vitest/browser webdriverio配置
要在 Vitest 配置中激活浏览器模式,请将 browser.enabled 字段设置为 true。以下是使用 browser 字段的配置示例:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
browser: {
provider: 'playwright', // 或 'webdriverio'
enabled: true,
// 至少需要一个实例
instances: [{ browser: 'chromium' }],
},
},
});INFO
Vitest 分配端口 63315 以避免与开发服务器冲突,从而允许您并行运行两者。您可以使用 browser.api 选项更改此设置。
自 Vitest 2.1.5 起,CLI 不再自动打印 Vite URL。在监听模式下运行时,您可以按“b”打印 URL。
如果您之前没有使用过 Vite,请确保已安装并配置了您所用框架的 Vite 插件。某些框架可能需要额外配置才能正常工作,请查阅其 Vite 相关文档以确认。
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
browser: {
enabled: true,
provider: 'playwright',
instances: [{ browser: 'chromium' }],
},
},
});import { defineConfig } from 'vitest/config';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
test: {
browser: {
enabled: true,
provider: 'playwright',
instances: [{ browser: 'chromium' }],
},
},
});import { defineConfig } from 'vitest/config';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
plugins: [svelte()],
test: {
browser: {
enabled: true,
provider: 'playwright',
instances: [{ browser: 'chromium' }],
},
},
});import { defineConfig } from 'vitest/config';
import solidPlugin from 'vite-plugin-solid';
export default defineConfig({
plugins: [solidPlugin()],
test: {
browser: {
enabled: true,
provider: 'playwright',
instances: [{ browser: 'chromium' }],
},
},
});import { defineConfig } from 'vitest/config';
import marko from '@marko/vite';
export default defineConfig({
plugins: [marko()],
test: {
browser: {
enabled: true,
provider: 'playwright',
instances: [{ browser: 'chromium' }],
},
},
});如果您需要使用基于 Node 的运行器运行某些测试,可以定义一个 projects 选项,其中包含针对不同测试策略的单独配置:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
projects: [
{
test: {
// 基于文件的约定示例,
// 这并非强制要求
include: [
'tests/unit/**/*.{test,spec}.ts',
'tests/**/*.unit.{test,spec}.ts',
],
name: 'unit',
environment: 'node',
},
},
{
test: {
// 基于文件的约定示例,
// 这并非强制要求
include: [
'tests/browser/**/*.{test,spec}.ts',
'tests/**/*.browser.{test,spec}.ts',
],
name: 'browser',
browser: {
enabled: true,
instances: [{ browser: 'chromium' }],
},
},
},
],
},
});浏览器选项类型
Vitest 中的浏览器选项取决于所选的提供者。如果您传递 --browser 但未在配置文件中指定其名称,Vitest 将会失败。可用选项:
webdriverio支持以下浏览器:firefoxchromeedgesafari
playwright支持以下浏览器:firefoxwebkitchromium
TypeScript
默认情况下,TypeScript 不识别提供者选项和额外的 expect 属性。如果您不使用任何提供者,请确保在您的测试、设置文件 或 配置文件 中引用 @vitest/browser/matchers,以获取额外的 expect 定义。如果您使用自定义提供者,请确保将 @vitest/browser/providers/playwright 或 @vitest/browser/providers/webdriverio 添加到同一文件中,以便 TypeScript 可以获取自定义选项的定义:
/// <reference types="@vitest/browser/matchers" />/// <reference types="@vitest/browser/providers/playwright" />/// <reference types="@vitest/browser/providers/webdriverio" />或者,您也可以将它们添加到 tsconfig.json 文件中的 compilerOptions.types 字段。请注意,在此字段中进行任何指定将禁用 @types/* 包的自动加载。
{
"compilerOptions": {
"types": ["@vitest/browser/matchers"]
}
}{
"compilerOptions": {
"types": ["@vitest/browser/providers/playwright"]
}
}{
"compilerOptions": {
"types": ["@vitest/browser/providers/webdriverio"]
}
}浏览器兼容性
Vitest 使用 Vite 开发服务器 运行测试,因此我们只支持 esbuild.target 选项(默认为 esnext)所指定的功能。
默认情况下,Vite 针对支持原生 ES 模块、原生 ESM 动态导入 和 import.meta 的浏览器。此外,我们还利用 BroadcastChannel 在 iframe 之间进行通信:
- Chrome >=87
- Firefox >=78
- Safari >=15.4
- Edge >=88
运行测试
当您在浏览器选项中指定浏览器名称时,Vitest 默认尝试使用 preview 模式运行指定的浏览器,并在其中执行测试。如果您不想使用 preview 模式,可以通过 browser.provider 选项配置自定义浏览器提供者。
要使用 CLI 指定浏览器,请使用 --browser 标志后跟浏览器名称,如下所示:
npx vitest --browser=chromium或者您可以使用点表示法向 CLI 传递浏览器选项:
npx vitest --browser.headlessWARNING
自 Vitest 3.2 起,如果您在配置中没有 browser 选项但指定了 --browser 标志,Vitest 将会失败,因为它无法假定该配置是针对浏览器测试而非 Node.js 测试的。
默认情况下,Vitest 会自动打开浏览器 UI 以供开发使用。您的测试将在中央的 iframe 中运行。您可以通过选择所需的尺寸、在测试中调用 page.viewport 或在配置中设置默认值来配置视口。
无头模式
无头模式是浏览器模式下的一个可用选项。在无头模式下,浏览器在后台运行,没有用户界面,这使其非常适合运行自动化测试。Vitest 中的无头选项可以设置为布尔值来启用或禁用无头模式。
在无头模式下,Vitest 不会自动打开 UI。如果您想继续使用 UI 但仍希望测试以无头模式运行,您可以安装 @vitest/ui 包并在运行 Vitest 时传递 --ui 标志。
以下是启用无头模式的配置示例:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
browser: {
provider: 'playwright',
enabled: true,
headless: true,
},
},
});您还可以使用 CLI 中的 --browser.headless 标志设置无头模式,如下所示:
npx vitest --browser.headless在这种情况下,Vitest 将使用 Chrome 浏览器以无头模式运行。
WARNING
无头模式默认不可用。您需要使用 playwright 或 webdriverio 提供者才能启用此功能。
示例
默认情况下,您不需要任何外部包即可在浏览器模式下工作:
import { expect, test } from 'vitest';
import { page } from '@vitest/browser/context';
import { render } from './my-render-function.js';
test('properly handles form inputs', async () => {
render(); // 挂载 DOM 元素
// 断言初始状态。
await expect
.element(page.getByText('Hi, my name is Alice'))
.toBeInTheDocument();
// 通过查询关联的标签获取输入 DOM 节点。
const usernameInput = page.getByLabelText(/username/i);
// 将名称输入到输入框中。这已验证输入是否正确填充,
// 无需手动检查值。
await usernameInput.fill('Bob');
await expect
.element(page.getByText('Hi, my name is Bob'))
.toBeInTheDocument();
});然而,Vitest 还提供了开箱即用的组件渲染包,用于多个流行框架:
vitest-browser-vue用于渲染 Vue 组件vitest-browser-svelte用于渲染 Svelte 组件vitest-browser-react用于渲染 React 组件
社区包可用于其他框架:
vitest-browser-lit用于渲染 Lit 组件vitest-browser-preact用于渲染 Preact 组件
如果您的框架未被涵盖,请随意创建您自己的包——它仅仅是框架渲染器和 page.elementLocator API 的简单包装。我们将在本页添加其链接。请确保其名称以 vitest-browser- 开头。
除了渲染组件和定位元素,您还需要进行断言。Vitest 复刻了 @testing-library/jest-dom 库,以提供开箱即用的各种 DOM 断言。请阅读 断言 API 了解更多信息。
import { expect } from 'vitest';
import { page } from '@vitest/browser/context';
// 元素渲染正确
await expect.element(page.getByText('Hello World')).toBeInTheDocument();Vitest 提供了一个 Context API,其中包含少量可能对您的测试有用的实用程序。例如,如果您需要执行交互操作,例如单击元素或在输入框中键入文本,您可以使用 @vitest/browser/context 中的 userEvent。请阅读 交互 API 了解更多信息。
import { page, userEvent } from '@vitest/browser/context';
await userEvent.fill(page.getByLabelText(/username/i), 'Alice');
// 或者直接使用 locator.fill
await page.getByLabelText(/username/i).fill('Alice');import { render } from 'vitest-browser-vue';
import Component from './Component.vue';
test('properly handles v-model', async () => {
const screen = render(Component);
// 断言初始状态。
await expect
.element(screen.getByText('Hi, my name is Alice'))
.toBeInTheDocument();
// 通过查询关联的标签获取输入 DOM 节点。
const usernameInput = screen.getByLabelText(/username/i);
// 将名称输入到输入框中。这已验证输入是否正确填充,
// 无需手动检查值。
await usernameInput.fill('Bob');
await expect
.element(screen.getByText('Hi, my name is Bob'))
.toBeInTheDocument();
});import { render } from 'vitest-browser-svelte';
import { expect, test } from 'vitest';
import Greeter from './greeter.svelte';
test('greeting appears on click', async () => {
const screen = render(Greeter, { name: 'World' });
const button = screen.getByRole('button');
await button.click();
const greeting = screen.getByText(/hello world/iu);
await expect.element(greeting).toBeInTheDocument();
});import { render } from 'vitest-browser-react';
import Fetch from './fetch';
test('loads and displays greeting', async () => {
// 将 React 元素渲染到 DOM 中
const screen = render(<Fetch url="/greeting" />);
await screen.getByText('Load Greeting').click();
// 等待,若找不到元素则抛出错误
const heading = screen.getByRole('heading');
// 断言警报消息是正确的
await expect.element(heading).toHaveTextContent('hello there');
await expect.element(screen.getByRole('button')).toBeDisabled();
});import { render } from 'vitest-browser-lit';
import { html } from 'lit';
import './greeter-button';
test('greeting appears on click', async () => {
const screen = render(html`<greeter-button name="World"></greeter-button>`);
const button = screen.getByRole('button');
await button.click();
const greeting = screen.getByText(/hello world/iu);
await expect.element(greeting).toBeInTheDocument();
});import { render } from 'vitest-browser-preact';
import { createElement } from 'preact';
import Greeting from '.Greeting';
test('greeting appears on click', async () => {
const screen = render(<Greeting />);
const button = screen.getByRole('button');
await button.click();
const greeting = screen.getByText(/hello world/iu);
await expect.element(greeting).toBeInTheDocument();
});Vitest 并非开箱即用地支持所有框架,但您可以使用外部工具来运行这些框架的测试。我们也鼓励社区创建自己的 vitest-browser 包装器——如果您有,欢迎将其添加到上述示例中。
对于不支持的框架,建议使用 testing-library 包:
@solidjs/testing-library用于渲染 Solid 组件@marko/testing-library用于渲染 Marko 组件
您还可以在 browser-examples 仓库中查看更多示例。
WARNING
testing-library 提供了一个名为 @testing-library/user-event 的包。我们不建议直接使用它,因为它模拟事件而非实际触发——相反,请使用从 @vitest/browser/context 导入的 userEvent,它在底层使用 Chrome DevTools Protocol 或 Webdriver(取决于提供者)。
// 基于 @testing-library/solid API
// https://testing-library.com/docs/solid-testing-library/api
import { render } from '@testing-library/solid';
it('uses params', async () => {
const App = () => (
<>
<Route
path="/ids/:id"
component={() => (
<p>
Id:
{useParams()?.id}
</p>
)}
/>
<Route path="/" component={() => <p>Start</p>} />
</>
);
const { baseElement } = render(() => <App />, { location: 'ids/1234' });
const screen = page.elementLocator(baseElement);
await expect.screen(screen.getByText('Id: 1234')).toBeInTheDocument();
});// 基于 @testing-library/marko API
// https://testing-library.com/docs/marko-testing-library/api
import { render, screen } from '@marko/testing-library';
import Greeting from './greeting.marko';
test('renders a message', async () => {
const { baseElement } = await render(Greeting, { name: 'Marko' });
const screen = page.elementLocator(baseElement);
await expect.element(screen.getByText(/Marko/)).toBeInTheDocument();
expect(container.firstChild).toMatchInlineSnapshot(`
<h1>Hello, Marko!</h1>
`);
});限制
线程阻塞对话框
使用 Vitest 浏览器时,请注意 alert 或 confirm 等线程阻塞对话框无法原生使用。这是因为它们会阻塞网页,这意味着 Vitest 无法继续与页面通信,从而导致执行挂起。
针对这种情况,Vitest 为这些 API 提供了带有默认返回值的模拟。这可以确保如果用户意外使用同步弹出式 Web API,执行不会被挂起。但仍建议用户模拟这些 Web API 以优化体验。请阅读 模拟 了解更多信息。