资讯动态

Playwright GenericAssertions 全解析:expect 通用值断言的完整用法与底层实现

发布时间:2026/9/5 21:53:25 来源:尧图企业网站定制
Playwright GenericAssertions 全解析expect 通用值断言的完整用法与底层实现【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright本文基于 Playwright 官方 API 文档中的 GenericAssertions 类自 v1.9 起可用仅适用于 JavaScript系统讲解expect()对任意值做断言的完整方法集toBe/toEqual/toStrictEqual三层相等性语义、resolves/rejects/not修饰符、比较器、模式匹配器asymmetric matchers并深入packages/playwright/src/matchers/下的源码说明每个断言在运行时如何执行、失败时如何给出诊断信息。读完后你应能准确选择断言方法、理解各参数取值与默认值并知道 Playwright 内置断言与 Locator 断言的边界。一、GenericAssertionsexpect 的“值断言”底座GenericAssertions 类提供对测试中任意值做断言的方法。文档给出的最小用法import { test, expect } from playwright/test; test(assert a value, async ({ page }) { const value 1; expect(value).toBe(2); });expect(value)返回的就是一个 GenericAssertions 实例在类型层面表现为interface GenericAssertionsR见 test.d.ts。从源码结构看playwright/test导出的是一个由createExpect()构造的函数它把文档中列出的全部模式匹配器挂到expect函数本身上expect.any/expect.anything/expect.arrayContaining/expect.arrayOf/expect.closeTo/expect.objectContaining/expect.stringContaining/expect.stringMatching的挂载见 expect.ts文档中出现的expectGeneric属于较老版本的 API 入口描述当前仓库中对外暴露的统一入口就是这个expect内置匹配器集合在 expect.ts 的allBuiltinMatchers中定义expectMatchers即 GenericAssertions 的主体来自 expectLibrary.tstoThrow/toThrowErrorcreateThrowMatcher生成 Playwright 特有的异步匹配器。需要注意的边界toBeVisible、toHaveText等匹配器只对Locator/Page等对象有效属于 class-locatorassertions.md 与 class-pageassertions.md 的范畴本文聚焦 GenericAssertions 对纯值的断言。二、修饰符not、resolves、rejects文档为 GenericAssertions 定义了三个返回自身类型的属性它们构成断言的链式前置修饰符。2.1 not断言取反const value 1; expect(value).not.toBe(2);not并不实现一套反向逻辑而是把元信息里的isNot位翻转后重新包装每个匹配器。见 expect.ts 中createMatchers对result.not[name]的构造以及最终判定处的result.pass !!info.isNotexpect.ts——匹配器自身只报告“原始条件是否成立”取反由外层统一完成因此每个匹配器都能天然获得.not变体。2.2 resolves解包已完成的 Promise对已 fulfill 的 Promiseresolves取出其值后继续链式匹配若 Promise 被 reject断言失败test(resolves to lemon, async () { await expect(Promise.resolve(lemon)).resolves.toBe(lemon); });2.3 rejects解包被拒绝 Promise 的原因若 Promise 被正常 fulfill断言失败被 reject 时取出 reason 继续匹配test(rejects to octopus, async () { await expect(Promise.reject(new Error(octopus))).rejects.toThrow(octopus); });两者的运行时行为在 expect.ts 的invokeMatcher中可见若实际值不是 Promise直接返回失败并生成createExpectedPromiseMessage文案resolves分支在.then的 rejected 回调里生成createExpectedToResolveMessagerejects分支则在 fulfilled 时生成createExpectedToRejectMessage、在 rejected 时用error作为匹配器的actual执行匹配。一个源码细节对于resolves/rejects链toThrow/toThrowError会使用promiseThrowMatchersexpect.ts中专门构造的 Promise 版 throw 匹配器这也是文档示例里rejects.toThrow(octopus)能对 rejection reason 断言的原因。三、相等性断言toBe、toBeCloseTo、toEqual、toStrictEqual这是 GenericAssertions 中语义差异最大、也最容易被用错的一组。3.1 toBe(expected)Object.is 引用相等expected any。调用Object.is比较对象按引用而非内容比较类似严格相等const value { prop: 1 }; expect(value).toBe(value); expect(value).not.toBe({}); expect(value.prop).toBe(1);源码中toBe的实现是一行核心逻辑Object.is(received, expected)expectLibrary.ts。更有价值的是其失败提示当两个值引用不同但“深度相等”时匹配器会额外跑一次toStrictEqual/toEqual语义的比较expectLibrary.ts并在报错信息中提示If it should pass with deep equality, replace toBe with toStrictEqual或 toEqual——这是 Playwright 对断言误用的自动化诊断。3.2 toBeCloseTo(expected, numDigits?)浮点数近似相等expected float期望值。numDigits ?int小数点后必须相等的位数。expect(0.1 0.2).not.toBe(0.3); expect(0.1 0.2).toBeCloseTo(0.3, 5);从源码看expectLibrary.tsprecision的默认值为 2即numDigits省略时保留两位精度判定逻辑是|expected - received| 10^(-precision) / 2并特判了正负无穷的比较。另外若received或expected不是 number 类型会抛出带类型提示的 TypeError而不是静默失败。3.3 toEqual(expected)深度相等非严格expected any。递归比较所有字段不比较对象引用const value { prop: 1 }; expect(value).toEqual({ prop: 1 });文档特别强调两条规则若要确认两个对象是同一实例应改用toBetoEqual忽略值为undefined的属性和数组项也不要求对象类型严格一致。更严格的匹配用toStrictEqual。3.4 toStrictEqual(expected)深度相等且类型严格expected any。在深度相等的基础上额外检查类型与toEqual的差异有三点带undefined值的键也会被检查{ a: undefined, b: 2 }不匹配{ b: 2 }检查数组稀疏性[, 1]不匹配[undefined, 1]检查对象类型相等字段a、b相同的类实例与字面量对象不相等。const value { prop: 1 }; expect(value).toStrictEqual({ prop: 1 });这些额外规则对应源码中的toStrictEqualTesters——iterableEquality、typeEquality、sparseArrayEquality、arrayBufferEqualityexpectLibrary.tstoStrictEqual匹配器把这一组 tester 附加到通用equals函数上expectLibrary.ts。四、类型与状态断言以下方法都不需要期望值只检查“值的类别/状态”适合在测试中做防御性校验。方法语义文档示例toBeDefined()值不是undefinedexpect(null).toBeDefined()toBeNull()值恰好是nullexpect(null).toBeNull()toBeUndefined()值恰好是undefinedexpect(undefined).toBeUndefined()toBeFalsy()布尔语境下为假false、0、、null、undefined、NaN之一expect(null).toBeFalsy()toBeTruthy()布尔语境下为真即除上述假值外的一切expect({ example: value }).toBeTruthy()toBeNaN()值是NaNexpect(NaN).toBeNaN()toBeInstanceOf(expected)expected Function用instanceof检查类实例expect(page).toBeInstanceOf(Page)toBeTruthy/toBeFalsy的源码实现就是对received做布尔转换后取/不取反expectLibrary.ts与文档列举的假值集合完全一致。五、数值比较断言四个方法均支持number或bigint方法语义参数expected float\|biginttoBeGreaterThan(expected)value expected比较对象toBeGreaterThanOrEqual(expected)value expected比较对象toBeLessThan(expected)value expected比较对象toBeLessThanOrEqual(expected)value expected比较对象const value 42; expect(value).toBeGreaterThan(1); expect(value).toBeGreaterThanOrEqual(42); expect(value).toBeLessThan(100); expect(value).toBeLessThanOrEqual(42);六、字符串断言toContain子串、toMatch6.1 toContain(expected)字符串重载expected string检查字符串值是否包含期望子串区分大小写const value Hello, World; expect(value).toContain(World); expect(value).toContain(,);6.2 toMatch(expected)expected RegExp|string检查字符串是否匹配正则const value Is 42 enough?; expect(value).toMatch(/Is \d enough/);七、集合断言toContain、toContainEqual、toHaveLength、toHaveProperty7.1 toContain(expected)集合重载expected any。值必须是Array或Set且包含期望项。与字符串重载共用方法名由值的类型区分行为const value [1, 2, 3]; expect(value).toContain(2); expect(new Set(value)).toContain(2);从源码结构看匹配器内部把Array、Set以及 DOM 的NodeList/DOMTokenList等都归为“可包含迭代器”expectLibrary.ts因此断言集合成员时用的是引用/原始值相等。7.2 toContainEqual(expected)expected any。值是Array或Set且包含与期望值深度相等的项。对对象而言它递归比较字段而不是像toContain集合重载那样按引用比较对原始值则与toContain等价const value [ { example: 1 }, { another: 2 }, { more: 3 }, ]; expect(value).toContainEqual({ another: 2 }); expect(new Set(value)).toContainEqual({ another: 2 });7.3 toHaveLength(expected)expected int。检查值的.length属性等于期望值适用于数组与字符串expect(Hello, World).toHaveLength(12); expect([1, 2, 3]).toHaveLength(3);7.4 toHaveProperty(keyPath, expected?)keyPath string属性路径。用点号a.b检查嵌套属性用下标a[2]检查嵌套数组项。expected ?any可选的期望值按toEqual相同的递归规则比较。const value { a: { b: [42] }, c: true, }; expect(value).toHaveProperty(a.b); expect(value).toHaveProperty(a.b, [42]); expect(value).toHaveProperty(a.b[0], 42); expect(value).toHaveProperty(c); expect(value).toHaveProperty(c, true);八、toMatchObject 与 toThrow部分匹配与异常断言8.1 toMatchObject(expected)expected Object|Array。对值做“深度相等”检查但允许值中多出 expected 没有的属性用于只校验对象属性的一个子集——与toEqual的全量匹配不同。比较数组时长度必须一致且逐项递归检查const value { a: 1, b: 2, c: true }; expect(value).toMatchObject({ a: 1, c: true }); expect(value).toMatchObject({ b: 2, c: true }); expect([{ a: 1, b: 2 }]).toMatchObject([{ a: 1 }]);8.2 toThrow(expected?) / toThrowError(expected?)expected ?any。调用传入的函数并断言其抛错可选地把抛出的错误与期望值比较。允许的期望值形态及其匹配规则正则 —— 错误消息应匹配该模式字符串 —— 错误消息应包含该子串Error 对象 —— 错误消息应与该对象的message属性相等Error 类 —— 错误对象应是该类的实例。expect(() { throw new Error(Something bad); }).toThrow(); expect(() { throw new Error(Something bad); }).toThrow(/something/); expect(() { throw new Error(Something bad); }).toThrow(Error);toThrowError是toThrow的别名参数语义相同expect(() { throw new Error(Something bad); }).toThrowError();这两种写法在allBuiltinMatchers中由createThrowMatcher(toThrow)/createThrowMatcher(toThrowError)生成expect.ts期望值到匹配策略的分派逻辑正则/字符串/Error 对象/类/非对称匹配器见 expectLibrary.ts。九、模式匹配器asymmetric matchersGenericAssertions 提供一组用于在toEqual内部做模式匹配的辅助构造器。它们必须放在toEqual的期望值里使用配合expect.前缀而非直接调用匹配器语义参数any(constructor)匹配由该构造函数创建的任意对象实例或对应原始类型的值constructor Function如ExampleClass或装箱类型Numberanything()匹配除null和undefined外的一切无arrayContaining(expected)匹配包含期望数组全部元素顺序任意的数组被匹配数组可以是超集expected Arrayany接收值的子集arrayOf(constructor)匹配由该构造函数/原始类型构成的对象数组自 v1.57 起constructor FunctioncloseTo(expected, numDigits?)期望值内部对浮点数做近似相等只比两个数字时优先toBeCloseToexpected floatnumDigits ?intobjectContaining(expected)匹配包含并匹配期望对象全部属性的对象允许被匹配对象多出属性属性本身还可以是匹配器expected ObjectstringContaining(expected)匹配包含期望子串的字符串expected stringstringMatching(expected)匹配自身又匹配期望模式的字符串expected string\|RegExp9.1 典型示例完整继承自文档any// Match instance of a class. class Example {} expect(new Example()).toEqual(expect.any(Example)); // Match any number. expect({ prop: 1 }).toEqual({ prop: expect.any(Number) }); // Match any string. expect(abc).toEqual(expect.any(String));anythingconst value { prop: 1 }; expect(value).toEqual({ prop: expect.anything() }); expect(value).not.toEqual({ otherProp: expect.anything() });arrayContainingexpect([1, 2, 3]).toEqual(expect.arrayContaining([3, 1])); expect([1, 2, 3]).not.toEqual(expect.arrayContaining([1, 4]));arrayOf// Match instance of a class. class Example {} expect([new Example(), new Example()]).toEqual(expect.arrayOf(Example)); // Match any string. expect([a, b, c]).toEqual(expect.arrayOf(String));closeToexpect({ prop: 0.1 0.2 }).not.toEqual({ prop: 0.3 }); expect({ prop: 0.1 0.2 }).toEqual({ prop: expect.closeTo(0.3, 5) });objectContaining属性本身可继续嵌套匹配器// Assert some of the properties. expect({ foo: 1, bar: 2 }).toEqual(expect.objectContaining({ foo: 1 })); // Matchers can be used on the properties as well. expect({ foo: 1, bar: 2 }).toEqual(expect.objectContaining({ bar: expect.any(Number) })); // Complex matching of sub-properties. expect({ list: [1, 2, 3], obj: { prop: Hello world!, another: some other value }, extra: extra, }).toEqual(expect.objectContaining({ list: expect.arrayContaining([2, 3]), obj: expect.objectContaining({ prop: expect.stringContaining(Hello) }), }));stringContaining/stringMatchingexpect(Hello world!).toEqual(expect.stringContaining(Hello)); expect(123ms).toEqual(expect.stringMatching(/\dm?s/)); // Inside another matcher. expect({ status: passed, time: 123ms, }).toEqual({ status: expect.stringMatching(/passed|failed/), time: expect.stringMatching(/\dm?s/), });9.2 模式匹配器的可组合性源码佐证这些匹配器不是独立 API而是被toEqual的递归相等比较“识别”出来的expect函数上的any/objectContaining等expect.ts生成的对象带有非对称匹配标记深度比较遇到它时调用其自定义比较逻辑。此外源码中还暴露了arrayNotContaining/objectNotContaining/stringNotMatching等反向版本expect.ts对应expect.not.arrayContaining(...)这类写法——即.not修饰符对模式匹配器同样生效。十、断言的运行时机制step、超时与 soft理解了匹配器语义后再看 Playwright 对“值断言”的运行时包装可以解释几个常见疑问每个断言都是一个 test step。callMatcherAsStep会为匹配器构造Expect toBe(…)之类的标题并通过testInfo._addStep注册expect.ts因此在 HTML 报告、test runner 输出中每条expect都可单独追溯其expected参数也会记录到 step 的params里。默认超时 5 秒。defaultExpectTimeout 5000expect.ts可由expect.configure({ timeout })或测试全局expect: { timeout }配置覆盖——该机制与resolves/rejects/poll的等待共用同一超时体系。soft 断言。expect.soft(value)失败时不立即中断测试而是把错误收集为 softErrorexpect.ts、[L350-L357]适合在一个用例里聚合多条通用值断言。与 Playwright 特有能力共存但互不干扰。Locator/Page 断言toBeVisible、toHaveText等在 expect.ts 中列为customAsyncMatchersGenericAssertions 的匹配器toBe、toEqual等在 expectLibrary.ts 中实现。两者在同一expect实例上注册类型系统则在 test.d.ts 中通过BaseMatchers GenericAssertions PlaywrightTest.Matchers SnapshotAssertions的交叉类型约束各自可接受的参数。十一、方法速查表方法签名一句话语义引入版本not属性 → GenericAssertions断言取反v1.9resolves属性 → GenericAssertions解包 fulfilled Promise 后链式匹配v1.9rejects属性 → GenericAssertions解包 rejected Promise 的 reason 后链式匹配v1.9toBe(expected: any)Object.is引用相等v1.9toBeCloseTo(expected: float, numDigits?: int)浮点近似相等默认精度 2v1.9toBeDefined()非undefinedv1.9toBeFalsy()false/0//null/undefined/NaN之一v1.9toBeGreaterThan(expected: float\|bigint)value expectedv1.9toBeGreaterThanOrEqual(expected: float\|bigint)value expectedv1.9toBeInstanceOf(expected: Function)instanceof检查v1.9toBeLessThan(expected: float\|bigint)value expectedv1.9toBeLessThanOrEqual(expected: float\|bigint)value expectedv1.9toBeNaN()值为NaNv1.9toBeNull()值为nullv1.9toBeTruthy()非假值v1.9toBeUndefined()值为undefinedv1.9toContain(expected: string)/(expected: any)子串包含区分大小写/ 集合成员包含v1.9toContainEqual(expected: any)集合中存在深度相等的成员v1.9toEqual(expected: any)深度相等忽略 undefined 属性与稀疏项v1.9toHaveLength(expected: int).length相等v1.9toHaveProperty(keyPath: string, expected?: any)属性路径存在且可选深度相等v1.9toMatch(expected: RegExp\|string)字符串匹配正则v1.9toMatchObject(expected: Object\|Array)深度相等但允许值多出属性数组长度须一致v1.9toStrictEqual(expected: any)深度相等 类型/undefined 键/稀疏性检查v1.9toThrow/toThrowError(expected?: any)函数抛错可选匹配消息/正则/类/实例v1.9any/anything模式匹配器任意构造器实例 / 任意非空值v1.9arrayContaining/arrayOf模式匹配器数组超集匹配 / 同构元素数组v1.9 / v1.57closeTo模式匹配器期望值内部的浮点近似匹配v1.9objectContaining模式匹配器对象属性子集匹配属性可再套匹配器v1.9stringContaining/stringMatching模式匹配器期望值内部的子串/正则匹配v1.9十二、小结GenericAssertions 让 Playwright 的expect同时扮演两个角色面向浏览器对象的自动重试断言以及面向任意值的完整测试断言库。选择方法时把握三条主线即可——引用相等用toBe内容相等用toEqual严格内容类型相等用toStrictEqualPromise 用resolves/rejects解包大对象局部校验用toMatchObject或objectContaining等模式匹配器。所有匹配器的具体判定逻辑都集中在 expectLibrary.ts 与 expect.ts 中可对照本文逐一验证其实现细节。【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价