浏览器模式 实验性
本页介绍 Vitest API 中实验性的浏览器模式功能。此功能允许您在浏览器中原生运行测试,并提供访问 window 和 document 等浏览器全局对象。此功能目前处于开发阶段,未来 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 提供程序,因为它依赖于模拟事件而不是使用 Chrome DevTools Protocol。
如果您还未使用这些工具中的任何一个,我们建议您从 Playwright 开始,因为它支持并行执行,可以加快测试运行速度。此外,Playwright 使用的 Chrome DevTools 协议通常比 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 标志或在 Vitest 配置文件中将 browser.enabled 字段设置为 true。以下是使用 browser 字段的配置示例:
export default defineConfig({
test: {
browser: {
provider: 'playwright', // 或 'webdriverio'
enabled: true,
name: 'chromium', // 必须指定浏览器名称
},
},
});INFO
Vitest 分配端口 63315 以避免与开发服务器冲突,从而允许您并行运行两者。您可以使用 browser.api 选项更改此设置。
自 Vitest 2.1.5 起,CLI 不再自动打印 Vite URL。在监听模式下运行时,您可以按“b”打印 URL。
如果您之前没有使用过 Vite,请确保您已安装框架的插件并在配置中指定。某些框架可能需要额外配置才能正常运行 - 请查看其 Vite 相关文档以确保。
import { defineConfig } from 'vitest/config';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
test: {
browser: {
enabled: true,
provider: 'playwright',
name: 'chromium',
},
},
});import { defineConfig } from 'vitest/config';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
plugins: [svelte()],
test: {
browser: {
enabled: true,
provider: 'playwright',
name: 'chromium',
},
},
});import { defineConfig } from 'vitest/config';
import solidPlugin from 'vite-plugin-solid';
export default defineConfig({
plugins: [solidPlugin()],
test: {
browser: {
enabled: true,
provider: 'playwright',
name: 'chromium',
},
},
});import { defineConfig } from 'vitest/config';
import marko from '@marko/vite';
export default defineConfig({
plugins: [marko()],
test: {
browser: {
enabled: true,
provider: 'playwright',
name: 'chromium',
},
},
});TIP
react 不需要插件即可工作,但 preact 需要额外配置才能使别名工作。
如果您需要使用基于 Node 的运行器运行某些测试,您可以定义一个工作区文件,其中包含针对不同测试策略的单独配置:
// vitest.workspace.ts
import { defineWorkspace } from 'vitest/config';
export default defineWorkspace([
{
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,
name: 'chrome',
},
},
},
]);提供程序配置
:::tabs key:provider == Playwright 您可以通过 providerOptions 字段配置 Vitest 如何启动浏览器和创建页面上下文:
export default defineConfig({
test: {
browser: {
providerOptions: {
launch: {
devtools: true,
},
context: {
geolocation: {
latitude: 45,
longitude: -30,
},
reducedMotion: 'reduce',
},
},
},
},
});如需获得类型提示,请将 @vitest/browser/providers/playwright 添加到 tsconfig.json 文件中的 compilerOptions.types。 == WebdriverIO 您可以通过 providerOptions 字段配置 Vitest 在启动浏览器时应使用哪些选项:
export default defineConfig({
test: {
browser: {
browser: 'chrome',
providerOptions: {
region: 'eu',
capabilities: {
browserVersion: '27.0',
platformName: 'Windows 10',
},
},
},
},
});如需获得类型提示,请将 @vitest/browser/providers/webdriverio 添加到 tsconfig.json 文件中的 compilerOptions.types。 :::
浏览器选项类型
Vitest 中的浏览器选项取决于提供程序。如果您传递 --browser 但未在配置文件中指定其名称,Vitest 会报错。可用选项:
webdriverio支持以下浏览器:firefoxchromeedgesafari
playwright支持以下浏览器:firefoxwebkitchromium
浏览器兼容性
Vitest 使用 Vite 开发服务器运行您的测试,因此我们仅支持 esbuild.target 选项(默认为 esnext)中指定的功能。
默认情况下,Vite 针对支持原生 ES Modules、原生 ESM 动态导入和 import.meta 的浏览器。除此之外,我们还利用 BroadcastChannel 在 iframe 之间进行通信:
- Chrome >=87
- Firefox >=78
- Safari >=15.4
- Edge >=88
运行测试
当您在浏览器选项中指定浏览器名称时,Vitest 将尝试默认使用 preview 运行指定的浏览器,然后在其中运行测试。如果您不想使用 preview,可以通过使用 browser.provider 选项配置自定义浏览器提供程序。
要使用 CLI 指定浏览器,请使用 --browser 标志,后跟浏览器名称,如下所示:
npx vitest --browser=chrome或者您可以使用点表示法向 CLI 提供浏览器选项:
npx vitest --browser.name=chrome --browser.headless默认情况下,Vitest 将自动打开浏览器 UI 进行开发。测试将在中央的 iframe 中运行。您可以通过选择首选尺寸、在测试中调用 page.viewport 或在配置中设置默认值来配置视口。
无头模式
无头模式是浏览器模式中可用的另一个选项。在无头模式下,浏览器会在后台运行,没有用户界面,这使得它对于运行自动化测试非常有用。Vitest 中的无头选项可以设置为布尔值以启用或禁用无头模式。
使用无头模式时,Vitest 不会自动打开 UI。如果您想继续使用 UI 但让测试在无头模式下运行,您可以安装 @vitest/ui 包并在运行 Vitest 时传递 --ui 标志。
以下是启用无头模式的配置示例:
export default defineConfig({
test: {
browser: {
provider: 'playwright',
enabled: true,
headless: true,
},
},
});您还可以使用 CLI 中的 --browser.headless 标志设置无头模式,如下所示:
npx vitest --browser.name=chrome --browser.headless在这种情况下,Vitest 将使用 Chrome 浏览器在无头模式下运行。
WARNING
无头模式默认不可用。您需要使用 playwright 或 webdriverio 提供程序才能启用此功能。
示例
Vitest 提供了一些开箱即用的软件包,用于渲染几种流行框架的组件:
vitest-browser-vue用于渲染 vue 组件vitest-browser-svelte用于渲染 svelte 组件vitest-browser-react用于渲染 react 组件
如果您的框架没有被支持,请随时创建您自己的软件包 - 它只是框架渲染器和 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 提供了一个上下文 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();
});Vitest 并非原生支持所有框架,但您可以使用外部工具运行这些框架的测试。我们也鼓励社区创建自己的 vitest-browser 包装器 - 如果您有,请随时将其添加到上面的示例中。
对于不受支持的框架,我们建议使用 testing-library 包:
@testing-library/preact用于渲染 preact 组件@solidjs/testing-library用于渲染 solid 组件@marko/testing-library用于渲染 marko 组件
WARNING
testing-library 提供了一个包 @testing-library/user-event。我们不建议直接使用它,因为它模拟事件而不是实际触发它们 - 相反,请使用从 @vitest/browser/context 导入的 userEvent,它在底层使用 Chrome DevTools Protocol 或 Webdriver(取决于提供程序)。
// 基于 @testing-library/preact 示例
// https://testing-library.com/docs/preact-testing-library/example
import { h } from 'preact';
import { page } from '@vitest/browser/context';
import { render } from '@testing-library/preact';
import HiddenMessage from '../hidden-message';
test('shows the children when the checkbox is checked', async () => {
const testMessage = 'Test Message';
const { baseElement } = render(<HiddenMessage>{testMessage}</HiddenMessage>);
const screen = page.elementLocator(baseElement);
// .query() 将返回元素,如果找不到则返回 null。
// .element() 将返回元素,如果找不到则抛出错误。
expect(screen.getByText(testMessage).query()).not.toBeInTheDocument();
// 查询可以接受正则表达式,以使您的选择器更具弹性,
// 能够应对内容调整和更改。
await screen.getByLabelText(/show/i).click();
await expect.element(screen.getByText(testMessage)).toBeInTheDocument();
});// 基于 @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();
await expect.element(container.firstChild).toMatchInlineSnapshot(`
<h1>Hello, Marko!</h1>
`);
});限制
线程阻塞对话框
使用 Vitest Browser 时,需要注意的是,不能原生使用 alert 或 confirm 等线程阻塞对话框。这是因为它们会阻塞网页,这意味着 Vitest 无法继续与页面通信,导致执行挂起。
在这种情况下,Vitest 为这些 API 提供了带有默认返回值的默认模拟。这确保了如果用户意外使用同步弹出式 Web API,执行不会挂起。但是,仍然建议用户模拟这些 Web API 以获得更好的体验。请参阅模拟了解更多信息。