[Playwright ][16]Playwright + TypeScript API 自动化测试实战:API测试

页面报错,到底是前端还是后端背锅?这篇用 Playwright 的 request 直连后端,带你从跑通第一条 /health 测试开始,一步步搞懂断言、异常输入、CRUD 。全程不用开浏览器,新手照着抄就能跑。


📌 先看这个:你能收获啥

  • 搞懂 UI 测试 vs API 测试 差在哪、啥时候该直接打接口
  • 跑通你的第一条 Playwright API 测试,并看懂每条断言在查什么
  • 写出"故意传错也通过"的异常用例,再也不怕看到 422
  • 改出一个自带清理的 CRUD 测试,单独跑也不留垃圾数据
  • 拿到一份发布前自查清单,别把 token、私人数据误提交

🗺️ 目录

  1. [UI 测试和 API 测试,差在哪](#UI 测试和 API 测试,差在哪)
  2. 认识现有文件
  3. 环境搞起来,跑通第一条测试
  4. 逐行读最简单的用例
  5. 从状态码进阶到返回内容
  6. [正常测试 vs 异常测试](#正常测试 vs 异常测试)
  7. 跟着项目加一条测试
  8. [搞懂 Recurring CRUD](#搞懂 Recurring CRUD)
  9. 挂了怎么排查
  10. 练习顺序
  • [附:发布前自查 & 下篇预告](#附:发布前自查 & 下篇预告)

1️⃣ UI 测试和 API 测试的区别

💡 一句话记住 :UI 测试点页面,API 测试直接打后端;普通接口用例根本不用开浏览器。

UI 测试是让浏览器去点页面;API 测试 用 Playwright 的 request 直接请求后端,再用 expect 查返回。

本项目两个地址用途不一样,别搞混:

地址 用途
http://localhost:5173 前端页面,给 UI 测试 用
http://localhost:5001 后端 API,给本文测试用

比如 request.get('/health') 在 API 项目里实际访问 http://localhost:5001/health。

为啥要测 API? 页面显示异常,锅可能在前端也可能在后端。单独验证后端能不能正确处理请求,相当于多一份"接口层证据",定位问题快得多。

先记几个词(新手向):

词 怎么理解
请求 告诉后端"我要查/操作啥"
响应 后端返回的状态码 + 数据
GET / POST / PUT / DELETE 查 / 提交(本项目用于创建·下载) / 更新(改名) / 删除类(解绑)
断言 自动判断"实际结果符不符合预期"的语句
200 / 404 / 422 成功 / 记录不存在 / 输入校验不通过

🖼️ 【项目结构 + API 配置】

截编辑器里 4 个手写测试文件 + playwright.e2e.config.ts 的 api 配置段;框出 testDir、testMatch、baseURL 三处。


2️⃣ 文件目录

文件 内容 学啥
tests/api/health.api.spec.ts 健康检查、OpenAPI、当前用户日志、Jobs 汇总 GET、JSON、基础断言
tests/api/auth.api.spec.ts 认证提供方、当前用户 状态码 + 用户字段断言
tests/api/job.api.spec.ts 汇总、详情、不存在的 Job、非法下载参数 正常/异常输入、返回结构
tests/api/recurring.api.spec.ts 查询、字段校验、完整 CRUD、清理 POST/PUT/DELETE、数据生命周期

配置入口是根目录 playwright.e2e.config.ts,api 项目这样配:

ts 复制代码
{
  name: 'api',
  testDir: './tests/api',
  testMatch: '**/*.api.spec.ts',
  use: { baseURL: 'http://localhost:5001' },
}

当前设置:单 worker、关完全并行、失败不重试;api 项目不依赖 UI 项目,所以单独选它不会先跑上传/页面清理。

🔑 划重点 :按业务拆文件 = 查认证问题直接看 auth.api.spec.ts;选 api 项目 = 请求走 5001。

单 worker 让执行顺序好观察、减少共享数据冲突,但它替代不了测试数据隔离------只是当前设计的取舍,不代表 API 测试都必须串行。


3️⃣ 跑通第一条测试

3.1 先确认测试能被发现

powershell 复制代码
npx playwright test --config=playwright.e2e.config.ts --project=api --list

--list 只收集用例、不发请求;能列出来 ≠ 后端或断言已通过。

3.2 只跑健康检查

powershell 复制代码
npx playwright test tests/api/health.api.spec.ts --config=playwright.e2e.config.ts --project=api -g 'GET /health returns 200'

命令拆解:

部分 作用
npx playwright test 调本项目 Playwright
tests/api/health.api.spec.ts 只选这个文件
--config=... 指定用哪份配置
--project=api 选 API 项目
-g '...' 按用例名筛,只跑匹配的

通过后逐步扩大代码范围:

powershell 复制代码
# 整个健康检查文件
npx playwright test tests/api/health.api.spec.ts --config=playwright.e2e.config.ts --project=api

# Jobs 测试
npx playwright test tests/api/job.api.spec.ts --config=playwright.e2e.config.ts --project=api

# 全部手写 API 测试(含会写数据的 CRUD)
npx playwright test tests/api/health.api.spec.ts tests/api/auth.api.spec.ts tests/api/job.api.spec.ts tests/api/recurring.api.spec.ts --config=playwright.e2e.config.ts --project=api

# 看最近一次 HTML 报告
npx playwright show-report

4️⃣ 最简单的用例

health.api.spec.ts 真例:

ts 复制代码
import { test, expect } from '@playwright/test';

test('GET /health returns 200', async ({ request }) => {
  const response = await request.get('/health');
  expect(response.status()).toBe(200);
});
  • test 定义用例,第一个参数是报告里显示的名
  • async ({ request }) 拿到 HTTP 请求对象
  • await 等请求完成、拿到响应再继续
  • response.status() 读 HTTP 状态码
  • expect(...).toBe(200) 是断言;不是 200 用例就挂

5️⃣ 从状态码进阶到返回内容

auth.api.spec.ts 不只查 200,还查用户:

ts 复制代码
const response = await request.get('/api/auth/me');
expect(response.status()).toBe(200);

const body = await response.json();
expect(body.authenticated).toBe(true);
expect(body.user.user_id).toBe('dev:1');

response.json() 把响应转成 JS 对象。注意:这条要求当前认证用户恰好是 dev:1。

Jobs 汇总进一步查结构:

ts 复制代码
const response = await request.get('/api/jobs/explorer/summary');
expect(response.status()).toBe(200);
const body = await response.json();

expect(body).toHaveProperty('user_id');
expect(Array.isArray(body.jobs)).toBe(true);
for (const job of body.jobs) {
  expect(job).toHaveProperty('job_id');
  expect(job).toHaveProperty('name');
  expect(job).toHaveProperty('requested');
  expect(typeof job.requested.cpu_cores).toBe('number');
}

6️⃣ 正常测试 vs 异常测试

预期来自当前测试代码;改接口时还要对照真实 API 规范和业务要求。

场景 请求 预期
服务健康 GET /health 200
已有 Job 详情 GET /api/jobs/job_1 200(依赖 job_1 存在且可访问)
不存在的 Job GET /api/jobs/nonexistent-playwright-job-999999 404
批量下载参数类型错 POST /api/jobs/download/multiple,job_ids 传字符串 422
创建周期任务缺名称 POST /api/recurring/jobs,只传 user_id 422
创建周期任务名称为空 rname: '' 422
改名缺必填 PUT /api/recurring/jobs/rename,只传新名称 422
解绑缺必填 DELETE /api/recurring/jobs/unbind,只传用户 422

异常测试不是"让测试报错",而是验证后端正确拒绝非法请求:

ts 复制代码
const response = await request.post('/api/jobs/download/multiple', {
  data: { job_ids: 'invalid-type' },
});
expect(response.status()).toBe(422);

data 是请求体。本例故意传错类型,收到 422 才算过。别看到 422 就当失败,也别为了过把 200/400/404/422 全加进允许范围。


7️⃣ Recurring CRUD

打开 recurring.api.spec.ts 的 Recurring Job CRUD lifecycle。CRUD = 增/查/改/删;本项目最后一步实际调的是 unbind(解绑),别据此推断数据库物理删除。

步骤 请求与请求体
创建 POST /api/recurring/jobs:rname、user_id:'dev:1'、assoc_job_ids:[]
读取 GET /api/recurring/alljobs,查新名存在
改名 PUT /api/recurring/jobs/rename:old_rname、new_rname、user_id
再读取 查新名在、旧名不在
解绑 DELETE /api/recurring/jobs/unbind:recurring_name、user_id
最后检查 再查列表,确认改名后名称不存在

测试用 pw-test-${Date.now()} 作名称,把几步放同一条用例里,自己管数据,不依赖别的测试先跑。

两个状态变量:created 标记是否还需清理,currentName 存当前应清理的名称。finally 会在中途断言失败时仍尝试解绑,降低数据残留概率。

当前实现的边界(写测试要心里有数):

  • 存在性检查用 JSON.stringify(body).includes(...) 搜名称,没按明确响应字段+用户精确匹配;确认真实结构后可改字段比较
  • 清理响应失败只输出警告,仍要看日志、查残留;不保证所有失败都清理成功
  • 此用例用 pw-test- 前缀,不能假设 UI 的 playwright-recurring-* 清理逻辑会管这些数据;API 项目也不依赖 UI cleanup

运行这条:

powershell 复制代码
npx playwright test tests/api/recurring.api.spec.ts --config=playwright.e2e.config.ts --project=api -g 'Recurring Job CRUD lifecycle'

9️⃣ 挂了怎么排查

现象 优先查
ECONNREFUSED、连不上 后端跑没、5001 暴露没、baseURL 对没
打到 5173,或 JSON 解析遇到 HTML 漏了 --config=... --project=api
/api/auth/me 不是 dev:1 或 401/403 后端认证环境是否符合前提;API 配置无显式登录
job_1 返 404 测试库是否存在且当前用户可访问
预期 422 实际 200 对照规范查字段校验;确认是否意外建数据需清理
CRUD 后列表仍有记录 查解绑响应、当前名称、用户 ID、清理警告
新测试没被发现 文件路径、.api.spec.ts 后缀、项目选择、-g 过滤
同一 API 测试出现三个浏览器项目 用了默认配置,改用本文 API 命令
ts 复制代码
console.log('Status:', response.status());
console.log('Body:', await response.text());
expect(response.status()).toBe(200);

🔟 本章内容

  1. 跑通 /health,理解请求地址和状态码
  2. 读 /openapi.json 用例,理解 JSON 和字段断言
  3. 跑 Auth 和 Jobs,识别 dev:1、job_1 的环境依赖
  4. 读非法下载参数用例,理解"预期拒绝也是成功"
    完成后,你应能解释:每条用例请求了啥、为啥预期这结果、依赖哪些数据、失败怎么清理排查。

尝试一下其他风格来写blog,如果流量低,就还是返回到老格式


下篇预告

这个完结后,准备做AI-tesintg,用qwen api,嘿嘿

相关推荐
小天源2 小时前
智叟云集系统 · 架构与职责分层
服务器·前端·数据库
Highcharts.js3 小时前
React 中原生支持使用 JSX 组件配置自定义表格 Highcharts Grid
前端·javascript·react.js·前端框架·jsx·highcharts·自定义表格
Java后端的Ai之路3 小时前
【AIAgent开发】-Agent工具封装成CLI(纯干货!!!)
java·javascript·人工智能·agent·cli
薛定谔的猫-菜鸟程序员4 小时前
Electron开发超详细教程(从入门到放弃)
前端·javascript·electron
最强小杰4 小时前
DeepSeek V4 Flash + Codex CLI 接入教程:原生 Responses API 适配,从配置到跑通收藏这篇就够了(2026)
java·前端·python·ai
静水leo4 小时前
使用 Whistle 拦截接口并模拟后端响应
前端
掘金者阿豪5 小时前
SQL Server数据迁移完成后,凌晨的数据库告警该怎么查?
前端·后端
JudithHuang5 小时前
React 嵌套路由与布局
前端·react.js·arcgis·react·react router