首页 / Playwright 入门教程 / API 测试直接发起请求 request

Playwright 入门教程

API 测试直接发起请求 request

本教程共 59 篇 · 第 34 篇 · 更新于 2026-08-04 · 约 12 分钟阅读

PlaywrightAPI测试requestapiRequest接口测试后端校验

本节目标:学完能直接用 Playwright 给后端发 HTTP 请求做接口测试,不打开浏览器也能验证服务端逻辑。

有些验证逻辑,根本不需要开浏览器。比如「创建一条数据,再查回来确认存在」,纯后端的事。Playwright 自带一个 request 夹具(Fixture),让你在 Node 里直接发请求。

为什么直接测接口

浏览器测试重在「界面表现得对不对」,接口测试重在「数据对不对」。两者互补。直接发请求的几点好处:

  1. 速度快,没有页面渲染开销。
  2. 能在 UI 测试前搭好服务端状态(比如先造一条数据)。
  3. 能在 UI 操作后校验数据库侧的真实结果。
Note

这套能力由 APIRequestContext(API 请求上下文)提供,测试里的 request 夹具就是它的一个实例。

关键一点:它完全不依赖浏览器。一个测试里只注入 request、不注入 page,Playwright 压根不会启动浏览器进程。所以纯接口测试跑起来是秒级的。

先配好公共项

接口通常要鉴权和统一域名。在配置里一次性写好,测试里就省心了:

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

export default defineConfig({
  use: {
    baseURL: 'https://api.github.com',          // 所有请求都发往这里
    extraHTTPHeaders: {
      'Accept': 'application/vnd.github.v3+json',
      'Authorization': `token ${process.env.API_TOKEN}`,  // 鉴权令牌
    },
  },
});

写一个接口测试

下面用 GitHub 接口演示:创建 issue,再查列表确认它真的进去了。

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

const REPO = 'test-repo-1';
const USER = 'github-username';

test('能创建一条 bug 报告', async ({ request }) => {
  const newIssue = await request.post(`/repos/${USER}/${REPO}/issues`, {
    data: { title: '[Bug] report 1', body: 'Bug description' },
  });

  expect(newIssue.ok()).toBeTruthy();             // 创建成功

  const issues = await request.get(`/repos/${USER}/${REPO}/issues`);
  expect(issues.ok()).toBeTruthy();
  expect(await issues.json()).toContainEqual(
    expect.objectContaining({ title: '[Bug] report 1' })
  );
});
Tip

request 会读配置里的 baseURL,所以路径写相对地址 /repos/... 即可,不用写全 URL。

用 beforeAll / afterAll 管前后置

接口测试常需要先建资源、最后销毁。用钩子包起来:

test.beforeAll(async ({ request }) => {
  const response = await request.post('/user/repos', { data: { name: REPO } });
  expect(response.ok()).toBeTruthy();
});

test.afterAll(async ({ request }) => {
  const response = await request.delete(`/repos/${USER}/${REPO}`);
  expect(response.ok()).toBeTruthy();
});

在 UI 测试里调接口

request 还能混在界面测试里用。比如先用接口造数据,再去页面确认它排在最前:

import { test, expect, type APIRequestContext } from '@playwright/test';

let apiContext: APIRequestContext;

test.beforeAll(async ({ playwright }) => {
  apiContext = await playwright.request.newContext({
    baseURL: 'https://api.github.com',
    extraHTTPHeaders: { 'Authorization': `token ${process.env.API_TOKEN}` },
  });
});

test.afterAll(async () => {
  await apiContext.dispose();   // 用完释放
});

test('最新创建的 issue 排在最前', async ({ page }) => {
  await apiContext.post(`/repos/${USER}/${REPO}/issues`, {
    data: { title: '[Feature] request 1' },
  });
  await page.goto(`https://github.com/${USER}/${REPO}/issues`);
  const firstIssue = page.locator(`a[data-hovercard-type='issue']`).first();
  await expect(firstIssue).toHaveText('[Feature] request 1');
});

验证后置条件

接口测试最有价值的地方,是校验「操作后服务端到底变了没有」。比如界面上提交了一张订单,你可以用接口把它查回来,确认它真的落库了,而不只是界面显示成功。

test('提交后服务端有这条记录', async ({ page, request }) => {
  await page.goto('/orders/new');
  await page.getByLabel('名称').fill('测试订单');
  await page.getByText('提交').click();

  const id = new URL(page.url()).pathname.split('/').pop();
  const resp = await request.get(`/api/orders/${id}`);
  expect(resp.ok()).toBeTruthy();
  expect(await resp.json()).toMatchObject({ name: '测试订单' });
});

这种「界面操作 + 接口校验」的组合,比单纯看界面更让人放心。

和浏览器共享登录态

request 有两种来源,行为差别很大:

  • 测试里注入的 request 夹具:挂在同一个 browser context 上,和浏览器共享 cookie。浏览器登录了,它也是登录态。
  • 自己 newContext() 建的:cookie 完全隔离,谁也不认谁。

最妙的是,你可以用接口登录,把状态存下来,再交给浏览器用:

// 注意:这里的 request 是从包里导入的 APIRequest 对象,不是测试夹具
import { request, chromium } from '@playwright/test';

const requestContext = await request.newContext({
  httpCredentials: { username: 'user', password: 'passwd' },
});
await requestContext.get('https://api.example.com/login');
await requestContext.storageState({ path: 'state.json' });  // 存登录态
await requestContext.dispose();

// 浏览器直接带着这个登录态打开
const browser = await chromium.launch();
const context = await browser.newContext({ storageState: 'state.json' });
Warning

别把两个 request 搞混。测试函数参数里解构出来的 request 是夹具,没有 newContext() 方法;能建新上下文的那个 request,要么从 @playwright/test 导入,要么走 playwright 夹具拿 playwright.request

Note

APIRequestContext 这套能力 Python 版同样具备,只是导入方式、命名风格和夹具的组织方式不一样。想看 Python 版怎么写,请见第 57 章,本章只讲 TypeScript。

什么时候该直接测接口

我的原则:纯数据逻辑用接口测,又快又准;涉及界面交互、文案、样式的,还是得开浏览器。

两者顺着一条测试链路串起来,前后端都覆盖,才是最稳的写法。

小结

  • request 夹具背后是 APIRequestContext,不启动浏览器,纯接口测试速度极快。
  • 公共项写进配置:baseURL 定域名,extraHTTPHeaders 放鉴权头,测试里就能用相对路径。
  • 断言用 response.ok() / response.status(),取数据用 await response.json()
  • 前后置资源用 test.beforeAll / test.afterAll 建和删,别让测试数据堆积。
  • 「接口造数据 + 界面验展示」「界面操作 + 接口验落库」,这两套组合最实用。
  • 夹具版 request 和浏览器共享 cookie;newContext() 建的是隔离的,两者别混用。
  • 接口登录拿到的 storageState 可以直接喂给浏览器,省掉走界面登录。

下一章解决一个前置问题:测试要访问的那个本地服务,怎么让它自己起来。