Skip to main content

高级:配置

配置对象

配置文件导出一个 TestConfig 对象。有关可用配置选项,请参阅 TestConfig 属性。

请注意,每个 测试项目 都可以提供自己的 选项,例如,两个项目可以通过提供不同的 testDir 来运行不同的测试。

这是一个定义公共超时和两个项目的示例。"Smoke" 项目运行一小部分没有重试的测试,而 "Default" 项目运行所有其他带有重试的测试。

// playwright.config.ts
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
timeout: 60000, // Timeout is shared between all tests.
projects: [
{
name: 'Smoke',
testMatch: /.*smoke.spec.ts/,
retries: 0,
},
{
name: 'Default',
testIgnore: /.*smoke.spec.ts/,
retries: 2,
},
],
};
export default config;

TestInfo 对象

测试函数、fixture 和钩子接收一个 TestInfo 参数,该参数提供有关当前运行测试的信息以及一些有用的实用程序,包括:

  • 有关测试的信息,例如 titleconfigproject
  • 有关测试执行的信息,例如 expectedStatusstatus
  • 测试工件实用程序,例如 outputPath()attach()

有关所有可用信息和实用程序,请参阅 TestInfo 方法和属性。

这是一个使用 TestInfo 将信息保存到文件的示例测试。

// example.spec.ts
import { test } from '@playwright/test';

test('my test needs a file', async ({ table }, testInfo) => {
// Do something with the table...
// ... and then save contents.
const filePath = testInfo.outputPath('table.dat');
await table.saveTo(filePath);
});

这是一个在测试失败时自动保存调试日志的示例 fixture。

// my-test.ts
import * as debug from 'debug';
import * as fs from 'fs';
import { test as base } from '@playwright/test';

// Note how we mark the fixture as { auto: true }.
// This way it is always instantiated, even if the test does not use it explicitly.
export const test = base.extend<{ saveLogs: void }>({
saveLogs: [ async ({}, use, testInfo) => {
const logs = [];
debug.log = (...args) => logs.push(args.map(String).join(''));
debug.enable('mycomponent');

await use();

if (testInfo.status !== testInfo.expectedStatus)
fs.writeFileSync(testInfo.outputPath('logs.txt'), logs.join('\n'), 'utf8');
}, { auto: true } ]
});

在测试期间启动开发 Web 服务器

要在测试期间启动服务器,请在 配置文件 中使用 webServer 选项。

如果在配置中指定了 port,测试运行器将在运行测试之前等待 127.0.0.1:port::1:port 可用。如果在配置中指定了 url,测试运行器将在运行测试之前等待该 url 返回 2xx、3xx、400、401、402 或 403 响应。

对于持续集成,您可能希望使用 reuseExistingServer: !process.env.CI 选项,该选项不会在 CI 上使用现有服务器。要查看 stdout,您可以设置 DEBUG=pw:webserver 环境变量。

port(但不是 url)会作为 testOptions.baseURL 传递给 Playwright。例如,端口 8080 会生成 baseURL 等于 http://localhost:8080

note

还建议在配置中指定 testOptions.baseURL,以便测试可以使用相对 URL。

// playwright.config.ts
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
webServer: {
command: 'npm run start',
url: 'http://localhost:3000/app/',
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI,
},
use: {
baseURL: 'http://localhost:3000/app/',
},
};
export default config;

现在您可以在导航页面时使用相对路径:

// test.spec.ts
import { test } from '@playwright/test';
test('test', async ({ page }) => {
// baseURL is set in the config to http://localhost:3000/app/
// This will navigate to http://localhost:3000/app/login
await page.goto('./login');
});

可以通过提供 webServer 配置数组来同时启动多个 Web 服务器(或后台进程)。有关其他示例和文档,请参阅 testConfig.webServer

全局设置和拆卸

要在运行所有测试之前设置一次某些内容,请在 配置文件 中使用 globalSetup 选项。全局设置文件必须导出一个接受配置对象的单个函数。此函数将在所有测试之前运行一次。

同样,使用 globalTeardown 在所有测试之后运行一次某些内容。或者,让 globalSetup 返回一个将用作全局拆卸的函数。您可以使用环境变量将数据(如端口号、身份验证令牌等)从全局设置传递到测试。

这是一个全局设置示例,它进行一次身份验证并在测试中重用身份验证状态。它使用配置文件中的 baseURLstorageState 选项。

// global-setup.ts
import { chromium, FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
const { baseURL, storageState } = config.projects[0].use;
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto(baseURL!);
await page.getByLabel('User Name').fill('user');
await page.getByLabel('Password').fill('password');
await page.getByText('Sign in').click();
await page.context().storageState({ path: storageState as string });
await browser.close();
}

export default globalSetup;

在配置文件中指定 globalSetupbaseURLstorageState

// playwright.config.ts
import type { PlaywrightTestConfig } from '@playwright/test';

const config: PlaywrightTestConfig = {
globalSetup: require.resolve('./global-setup'),
use: {
baseURL: 'http://localhost:3000/',
storageState: 'state.json',
},
};
export default config;

测试开始时已经通过身份验证,因为我们指定了由全局设置填充的 storageState

import { test } from '@playwright/test';

test('test', async ({ page }) => {
await page.goto('/');
// You are signed in!
});

您可以通过 process.env 设置环境变量,使全局设置文件中的任意数据在测试中可用。

// global-setup.ts
import { FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
process.env.FOO = 'some data';
// Or a more complicated data structure as JSON:
process.env.BAR = JSON.stringify({ some: 'data' });
}

export default globalSetup;

测试可以访问在全局设置中设置的 process.env 属性。

import { test } from '@playwright/test';

test('test', async ({ page }) => {
// environment variables which are set in globalSetup are only available inside test().
const { FOO, BAR } = process.env;

// FOO and BAR properties are populated.
expect(FOO).toEqual('some data');

const complexData = JSON.parse(BAR);
expect(BAR).toEqual({ some: 'data' });
});

在全局设置期间捕获失败的跟踪

在某些情况下,捕获全局设置期间遇到的失败的跟踪可能很有用。为此,您必须在设置中 开始跟踪,并且必须确保在抛出错误之前 停止跟踪(如果发生错误)。这可以通过将设置包装在 try...catch 块中来实现。这是一个扩展全局设置示例以捕获跟踪的示例。

// global-setup.ts
import { chromium, FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
const { baseURL, storageState } = config.projects[0].use;
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
try {
await context.tracing.start({ screenshots: true, snapshots: true });
await page.goto(baseURL!);
await page.getByLabel('User Name').fill('user');
await page.getByLabel('Password').fill('password');
await page.getByText('Sign in').click();
await context.storageState({ path: storageState as string });
await context.tracing.stop({
path: './test-results/setup-trace.zip',
})
await page.close();
} catch (error) {
await context.tracing.stop({
path: './test-results/failed-setup-trace.zip',
});
await page.close();
throw error;
}
}

export default globalSetup;

项目

Playwright Test 支持同时运行多个测试项目。这对于在多个配置中运行相同或不同的测试很有用。

相同的测试,不同的配置

这是一个在不同浏览器中运行相同测试的示例:

// playwright.config.ts
import { type PlaywrightTestConfig, devices } from '@playwright/test';

const config: PlaywrightTestConfig = {
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
};
export default config;

您可以运行所有项目或仅运行单个项目:

# Run both projects - each test will be run three times
npx playwright test

# Run a single project - each test will be run once
npx playwright test --project=chromium

不同的测试,不同的配置

每个项目都可以单独配置,并使用不同的选项运行不同的测试集。您可以使用 testProject.testDirtestProject.testMatchtestProject.testIgnore 来配置项目应该运行哪些测试。

这是一个运行具有不同测试和配置的项目的示例。"Smoke" 项目运行一小部分没有重试的测试,而 "Default" 项目运行所有其他带有重试的测试。

// playwright.config.ts
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
timeout: 60000, // Timeout is shared between all tests.
projects: [
{
name: 'Smoke',
testMatch: /.*smoke.spec.ts/,
retries: 0,
},
{
name: 'Default',
testIgnore: /.*smoke.spec.ts/,
retries: 2,
},
],
};
export default config;

您可以运行所有项目或仅运行单个项目:

# Run both projects
npx playwright test

# Run a single project
npx playwright test --project=Smoke

自定义项目参数

项目还可以用于使用自定义配置参数化测试 - 请查看 这个单独的指南

WorkerInfo 对象

根据配置和失败情况,Playwright Test 可能会使用不同数量的工作进程来运行所有测试。例如,Playwright Test 将始终在失败的测试后启动一个新的工作进程。

工作范围的 fixture 接收一个 WorkerInfo 参数,该参数描述当前的工作配置。有关可用的工作信息,请参阅 WorkerInfo 属性。

考虑一个示例,我们为每个工作进程运行一个新的 http 服务器,并使用 workerIndex 生成唯一的端口号:

// my-test.ts
import { test as base } from '@playwright/test';
import * as http from 'http';

// Note how we mark the fixture as { scope: 'worker' }.
// Also note that we pass empty {} first, since we do not declare any test fixtures.
export const test = base.extend<{}, { server: http.Server }>({
server: [ async ({}, use, workerInfo) => {
// Start the server.
const server = http.createServer();
server.listen(9000 + workerInfo.workerIndex);
await new Promise(ready => server.once('listening', ready));

// Use the server in the tests.
await use(server);

// Cleanup.
await new Promise(done => server.close(done));
}, { scope: 'worker' } ]
});

使用 expect.extend 添加自定义匹配器

Playwright Test 在底层使用 expect,该库具有使用 自定义匹配器 扩展它的功能。

在此示例中,我们在配置文件中添加了一个自定义的 toBeWithinRange 函数。

// playwright.config.ts
import { expect, PlaywrightTestConfig } from '@playwright/test';

expect.extend({
toBeWithinRange(received: number, floor: number, ceiling: number) {
const pass = received >= floor && received <= ceiling;
if (pass) {
return {
message: () => 'passed',
pass: true,
};
} else {
return {
message: () => 'failed',
pass: false,
};
}
},
});

const config: PlaywrightTestConfig = {};
export default config;

现在我们可以在测试中使用 toBeWithinRange

// example.spec.ts
import { test, expect } from '@playwright/test';

test('numeric ranges', () => {
expect(100).toBeWithinRange(90, 110);
expect(101).not.toBeWithinRange(0, 100);
});

对于 TypeScript,还要将以下内容添加到您的 global.d.ts。如果它不存在,您需要在存储库中创建它。确保您的 global.d.ts 通过 includecompilerOptions.typeRoots 选项包含在您的 tsconfig.json 中,以便您的 IDE 能够识别它。

JavaScript 不需要它。

// global.d.ts
export {};

declare global {
namespace PlaywrightTest {
interface Matchers<R, T> {
toBeWithinRange(a: number, b: number): R;
}
}
}