首页
/ Playwright APIResponseAssertions 完全解析:对 APIResponse 做响应状态断言(toBeOK 与 not 反转)

Playwright APIResponseAssertions 完全解析:对 APIResponse 做响应状态断言(toBeOK 与 not 反转)

2026-09-04 18:02:37作者:明树来

在 Playwright 的测试断言体系中,APIResponseAssertions 专门负责对 APIResponse 对象发起断言,最常用的场景就是用 expect(response).toBeOK() 校验 HTTP 响应状态码是否落在 200..299 区间。本文基于仓库中的 API 参考文档 class-apiresponseassertions.md 与底层匹配器实现 matchers.ts,完整覆盖该类的全部成员(toBeOKnot、Python 的 NotToBeOK),并结合 TypeScript 类型定义与测试用例,说明各语言绑定下的写法差异、失败时的错误信息构成,以及 not 反转断言的底层机制。

类定位:APIResponseAssertions 是 expect 对 APIResponse 的专用匹配器集合

根据 API 文档,APIResponseAssertionsv1.18 起提供(文档标注 since: v1.18),用于对 APIResponse 进行测试断言。典型入口是先通过 page.request.get() 之类的 APIRequestContext 方法拿到 APIResponse,再交给 expect:

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

test('navigates to login', async ({ page }) => {
  // ...
  const response = await page.request.get('https://playwright.dev');
  await expect(response).toBeOK();
});

test.d.ts 的类型定义可以看到该接口只有两个成员:toBeOK(): Promise<void>not: APIResponseAssertions。同时 AllMatchers 类型把它和 PageAssertionsLocatorAssertions 等并列合并(见 test.d.ts),并且只有当被断言对象 T extends APIResponse 时,这些断言才出现在类型上——也就是说,Playwright 的 expect 会根据传入对象的类型动态收窄可用匹配器,把 APIResponse 传给 expect 后,IDE 只会提示 API 响应相关的断言方法,不会出现 DOM 定位器断言的干扰。

各语言绑定的对应写法如下(与文档一致):

Java(通过 PlaywrightAssertions.assertThat 进入断言链):

// ...
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;

public class TestPage {
  // ...
  @Test
  void navigatesToLoginPage() {
    // ...
    APIResponse response = page.request().get("https://playwright.dev");
    assertThat(response).isOK();
  }
}

Python(async / sync 两种风格,方法名为蛇形命名 to_be_ok):

# async
from playwright.async_api import Page, expect

async def test_navigates_to_login_page(page: Page) -> None:
    # ..
    response = await page.request.get('https://playwright.dev')
    await expect(response).to_be_ok()
# sync
from playwright.sync_api import Page, expect

def test_navigates_to_login_page(page: Page) -> None:
    # ..
    response = page.request.get('https://playwright.dev')
    expect(response).to_be_ok()

C#(MSTest 风格,异步方法名带 Async 后缀):

using Microsoft.Playwright;
using Microsoft.Playwright.MSTest;

namespace PlaywrightTests;

[TestClass]
public class ExampleTests : PageTest
{
    [TestMethod]
    public async Task NavigatesToLoginPage()
    {
        var response = await Page.APIRequest.GetAsync("https://playwright.dev");
        await Expect(response).ToBeOKAsync();
    }
}

命名规律值得注意:同一语义的断言在四种语言中分别为 JS 的 toBeOK、Java 的 isOK(文档中标注了 alias-java: isOK)、Python 的 to_be_ok、C# 的 ToBeOKAsync。写跨语言测试或从其他框架迁移时,按这个映射关系查找 API 即可。

核心断言 toBeOK:状态码 200..299 区间校验

toBeOKv1.18 起可用,语义是:确保响应状态码处于 200..299 范围内(即成功类状态码,含 2xx 全部)。各语言用法:

await expect(response).toBeOK();
assertThat(response).isOK();
# async
await expect(response).to_be_ok()
# sync
expect(response).to_be_ok()
await Expect(response).ToBeOKAsync();

源码级实现:判定逻辑与失败信息

toBeOK 的 JavaScript 端实现在 matchers.ts:

export async function toBeOK(
  this: ExpectMatcherStateInternal,
  response: APIResponseEx
) {
  const matcherName = 'toBeOK';
  expectTypes(response, ['APIResponse'], matcherName);

  const contentType = response.headers()['content-type'];
  const isTextEncoding = contentType && isTextualMimeType(contentType);
  const [log, text] = (this.isNot === response.ok()) ? await Promise.all([
    response._fetchLog(),
    isTextEncoding ? response.text() : null
  ]) : [];

  const message = () => formatMatcherMessage(this.utils, {
    isNot: this.isNot,
    promise: this.promise,
    matcherName,
    receiver: 'response',
    expectation: '',
    log,
  }) + (text === null ? '' : `\nResponse text:\n${colors.dim(text?.substring(0, 1000) || '')}`);

  const pass = response.ok();
  return { message, pass };
}

从这段实现可以确认几个关键点:

  1. 类型守卫:expectTypes(response, ['APIResponse'], matcherName) 会先校验传入对象确实是 APIResponse。如果把 Locator 或普通对象传给 expect(...).toBeOK(),会在断言阶段直接报类型错误,而不是静默通过。
  2. 判定标准是 response.ok():pass = response.ok(),对应 HTTP 2xx 区间。它不会检查 3xx 重定向目标或 4xx/5xx,也不会等待重试——toBeOK 是一次性判定,响应对象本身在 page.request.get() 等调用返回时已经拿到完整状态码,断言不再有轮询等待行为。
  3. 失败时的诊断信息:仅当断言失败时(即 this.isNot === response.ok())才会额外收集两样东西:response._fetchLog()(底层 fetch 过程日志)和响应正文。正文只在 content-type 属于文本型 MIME(isTextualMimeType)时读取,并截断到前 1000 字符,以灰色(dim)追加在错误消息的 Response text: 段落中。这意味着测试报告里看到"status 500 + 部分响应体"的输出,正是来自这段拼接逻辑,对定位后端错误很有价值。
  4. isNot 的复用:同一个函数同时服务于 toBeOKnot.toBeOK,失败条件写成 this.isNot === response.ok()——即"断言方向"与"实际 ok 状态"一致时反而需要收集失败日志。这解释了为什么 not 不需要单独的 JS 匹配器实现。

类型定义与注册位置

toBeOK 的签名在 test.d.ts 中声明为 toBeOK(): Promise<void>,注释明确写着 "Ensures the response status code is within 200..299 range"。该函数在 expect.ts 中被导入并注册进 expect 的默认匹配器表(见第 61 行与第 189 行的 toBeOK 条目),因此 import { expect } from '@playwright/test' 后即可直接使用,无需手动 expect.extend

not 反转断言:检查"非成功"响应

not 属性自 v1.20 起提供(文档标注 langs: java, js, csharp),作用是让断言检查相反的条件——例如验证响应状态不是成功:

await expect(response).not.toBeOK();
assertThat(response).not().isOK();
await Expect(response).Not.ToBeOKAsync();

test.d.ts 可以看到它的类型是递归的自引用:not: APIResponseAssertions。这正是 JS 端能复用一个 toBeOK 函数同时支持正反两种断言的机制:not 只是翻转 ExpectMatcherState 上的 isNot 标志(匹配器实现中所有 this.isNot 判断都基于它,前面 matchers.ts 中的失败条件 this.isNot === response.ok() 即是最直接的体现)。Java 与 C# 端则是显式的链式方法/属性:.not().Not

not.toBeOK() 的典型用途:校验权限控制(401/403)、路由不存在(404)、限流(429)等"预期失败"场景。例如:

// 未携带 token 时应返回非 2xx
const response = await page.request.get('/api/protected');
await expect(response).not.toBeOK();

Python 特例:NotToBeOK 方法

由于 Python 绑定没有 .not 属性,文档为其提供了独立方法 NotToBeOK(async method,自 v1.19 起),语义就是 toBeOK 的反面:

await expect(response).not_to_be_ok()  # async
expect(response).not_to_be_ok()        # sync

这也是该文档列出的唯一 Python 专属条目(langs: python),与 JS 的 not 属性、Java 的 not()、C# 的 Not 一一对应。

测试用例中的实际用法

仓库的测试代码本身就展示了 toBeOK 的两种使用姿态:

小结与使用边界

成员 语言 最低版本 语义
toBeOK() / isOK() / to_be_ok() / ToBeOKAsync() js / java / python / csharp v1.18 状态码在 200..299
not / not() / Not js / java / csharp v1.20 反转断言方向
NotToBeOK()(not_to_be_ok) python v1.19 toBeOK 的反面

几点使用边界需要明确:

  • toBeOK 只判断 2xx,不跟随重定向语义、不检查响应体内容、不轮询重试;若需要等待,应在拿到响应前对业务行为做等待(如 waitForResponse),拿到 APIResponse 后断言即是一次性的。
  • 传入对象必须来自 APIRequestContext(如 page.request.get()context.request.get() 等)的 APIResponse,否则会被 expectTypes 类型检查拦截。
  • 断言失败时自动附带 fetch 日志与最多 1000 字符的文本响应体,这是内建行为,无需额外配置。
  • 各语言别名对照:JS toBeOK = Java isOK = Python to_be_ok = C# ToBeOKAsync;反转:JS not = Java not() = C# Not = Python not_to_be_ok

更多上下文可参考文档源文件 class-apiresponseassertions.md、类型声明 test.d.ts 与匹配器实现 matchers.ts

登录后查看全文
热门项目推荐
相关项目推荐