文档教程知识库【免费下载链接】til:memo: Today I Learned项目地址https://gitcode.com/gh_mirrors/ti/til点击查看免费下载本文基于 TILToday I Learned仓库中的 define-a-custom-jest-matcher 一篇系统讲解如何借助 Jest 的expect.extend()定义属于你自己的自定义匹配器Custom Matcher并给出一个可复制运行的toHaveMatchingId示例。读完本文你将掌握匹配器的返回契约pass与message、在测试中像内置匹配器一样使用自定义匹配器的方法以及借助 setup 文件全局注册、支持嵌套匹配等进阶技巧。为什么需要自定义匹配器Jest 内置的 expect 匹配器如toBe、toEqual、toContain已经能覆盖绝大多数测试场景。但在某些业务场景下你需要一套领域专属的断言语义例如只比较两个对象的id字段是否相等而不关心其余字段又例如校验一个数组里是否存在满足特定条件的元素。这些需求用内置匹配器往往要写出一长串临时逻辑可读性差、且无法复用。此时就可以定义自定义匹配器把如何判定通过/失败以及失败时输出什么信息封装在单个可复用的断言里。Jest 为此提供了官方入口 ——expect.extend()函数本文的核心示例正是围绕它展开的。认识 expect.extend() 与匹配器的返回契约expect.extend()接受一个对象对象的每个键就是一个自定义匹配器的名字键对应的值是该匹配器的实现函数。匹配器实现函数接收被测值received即expect(...)里传入的值以及你传入的其余参数本文示例中的expected。实现函数必须返回一个对象该对象包含两个关键字段pass: boolean—— 表示本次断言是否通过message: () string—— 一个返回字符串的函数用于在断言失败时生成给测试运行器展示的错误信息。从源码结构看Jest 拿到这个返回对象后会结合断言的否定形式如not.toHaveMatchingId决定最终向用户展示message()的哪一侧文案因此在pass: true分支里通常写的是预期不匹配却匹配了这类否定语义的提示。完整示例按 id 比较对象的 toHaveMatchingId 匹配器下面是原文档给出的完整实现 —— 一个只依据id属性判断两个对象是否相等的匹配器expect.extend({ toHaveMatchingId(recieved, expected) { const pass recieved.id expected.id; if (pass) { return { pass: true, message: () expected id:${expected.id} to not match actual id:${recieved.id} }; } else { return { pass: false, message: () expected id:${expected.id} to match actual id:${recieved.id} }; } } });拆解这段代码可以提炼出自定义匹配器的三要素匹配器名称这里的toHaveMatchingId即匹配器名字。遵循 Jest 惯例自定义匹配器一般以to开头便于与内置匹配器保持一致的阅读体验。判定逻辑函数体内的pass计算即核心判定。本例仅比对recieved.id expected.id说明匹配器实现完全可以是普通的 JavaScript 运算没有任何黑魔法。两个返回分支pass: true与pass: false各返回一个带message函数的对象。注意pass: true分支的文案是期望 id:xxx 不匹配实际 id:xxx它服务于not.toHaveMatchingId()这类否定断言pass: false分支才是正常失败时展示的期望 id:xxx 匹配实际 id:xxx。另外参数名recieved、expected本身并无特殊约定你可以随意命名只要与函数体内的使用保持一致即可。在测试中像内置匹配器一样使用定义完成之后自定义匹配器在测试里的用法与内置匹配器完全一致直接链式调用即可test(compare objects, () { expect({ id: 001 }).toHaveMatchingId({ id: 001 }); // ✅ 通过 expect({ id: 001 }).toHaveMatchingId({ id: 002 }); // ❌ 失败输出expected id:002 to match actual id:001 });可以看到当断言失败时Jest 会自动使用我们在message()中构造的文案进行提示信息量比笼统的对象不相等要友好得多。原文档还附带了一个可以在线运行的示例环境CodeSandbox读者可以边改边跑直观验证pass与message在不同输入下的表现。进阶让匹配器支持嵌套匹配自定义匹配器的判定逻辑虽然可以写任意 JavaScript 运算但直接使用或普通比较会丢失 Jest 强大的一等公民能力 ——嵌套匹配器asymmetric matchers。仓库中的另一篇 support-nested-matching-in-custom-jest-matchers 专门讲解了这个问题。考虑这样一个场景你想断言数组里包含某个满足条件的值如果只用some(value value containedValue)那么像下面这样使用expect.any(Number)就会失败尽管数组里确实有数字expect([a, 2, true]).toContainValue(expect.any(Number));解决方案是复用 Jest 内部使用的 Jasmine 比较工具equals它知道如何把原始值整数、布尔值乃至整个对象与嵌套的expect匹配器进行比对const { equals } require(expect/build/jasmineUtils); expect.extend({ toContainValue(receivedArray, containedValue) { const pass receivedArray.some(value equals(value, containedValue)); // return formatted pass/not-pass objects with messages return { ... } } });这里也印证了自定义匹配器的本质是返回{ pass, message }的判定函数在判定逻辑内部你可以自由组合普通运算与 Jest 的嵌套匹配能力。如何全局注册在 setup 文件中配置如果项目里多个测试文件都要用到自定义匹配器逐个文件顶部调用expect.extend()显然不合适。仓库中的 configure-jest-to-run-a-test-setup-file 一篇提供了更优雅的集中式方案在package.json或jest.config.js里配置一个测试 setup 文件让 Jest 在每个测试运行前先加载它// package.json { // ... jest: { setupTestFrameworkScriptFile: rootDirsrc/setupTests.js } }其中setupTestFrameworkScriptFile指向以rootDir项目根目录为基准的 setup 文件路径。你只要把本文的expect.extend({ ... })调用放进src/setupTests.js所有测试文件就能直接使用toHaveMatchingId了。这类 setup 文件同样适用于需要全局配置测试框架适配器的场景如 Enzyme。相关 Jest 技巧速览围绕自定义匹配器 高效测试TIL 仓库的 JavaScript 分类下还有多篇可组合使用的笔记mock-a-function-with-return-values-using-jest用jest.fn()与mockReturnValue()/mockReturnValueOnce()控制 mock 函数的返回值适合在自定义匹配器的测试场景里隔离依赖。tell-jest-to-focus-on-running-only-one-test用test.only()聚焦单个测试块调试匹配器判定逻辑时非常有用。turn-off-console-error-messages-in-a-test临时用jest.fn()替换console.error在刻意测试失败路径时保持输出干净。test-coverage-stats-with-jest用jest --coverage查看语句/分支/函数/行覆盖率帮助你发现哪些断言包括自定义匹配器还缺少用例覆盖。小结自定义匹配器是 Jest 扩展断言能力的第一方入口。通过expect.extend()你可以用普通 JavaScript 逻辑定义领域专属的判定规则如按id比较对象严格遵守返回{ pass, message }的契约分别构造通过与否的提示文案在message()里嵌入实际值让失败信息具备可读性借助 setup 文件全局注册配合嵌套匹配器与equals工具实现更复杂的断言语义。当内置匹配器表达不了你的业务断言时写一个自定义匹配器往往比在测试里堆临时逻辑更清晰、更可复用。输出文章 输出文章用 expect.extend() 为 Jest 定义自定义匹配器TIL 实战笔记本文基于 TILToday I Learned仓库中的 define-a-custom-jest-matcher 一篇系统讲解如何借助 Jest 的expect.extend()定义属于你自己的自定义匹配器Custom Matcher并给出一个可复制运行的toHaveMatchingId示例。读完本文你将掌握匹配器的返回契约pass与message、在测试中像内置匹配器一样使用自定义匹配器的方法以及借助 setup 文件全局注册、支持嵌套匹配等进阶技巧。为什么需要自定义匹配器Jest 内置的 expect 匹配器如toBe、toEqual、toContain已经能覆盖绝大多数测试场景。但在某些业务场景下你需要一套领域专属的断言语义例如只比较两个对象的id字段是否相等而不关心其余字段又例如校验一个数组里是否存在满足特定条件的元素。这些场景用内置匹配器往往要写出一长串临时逻辑可读性差且无法复用。此时就可以定义自定义匹配器把如何判定通过/失败以及失败时输出什么信息封装在单个可复用的断言里。Jest 为此提供了官方入口 ——expect.extend()函数本文的核心示例正是围绕它展开的。认识 expect.extend() 与匹配器的返回契约expect.extend()接受一个对象对象的每个键就是一个自定义匹配器的名字键对应的值是该匹配器的实现函数。匹配器实现函数接收被测值received即expect(...)里传入的值以及你传入的其余参数本文示例中的expected。实现函数必须返回一个对象该对象包含两个关键字段pass: boolean—— 表示本次断言是否通过message: () string—— 一个返回字符串的函数用于在断言失败时生成给测试运行器展示的错误信息。从匹配器的工作方式可以推断Jest 拿到这个返回对象后会结合断言的否定形式如not.toHaveMatchingId决定最终向用户展示message()的哪一侧文案因此在pass: true分支里通常写的是预期不匹配却匹配了这类否定语义的提示。完整示例按 id 比较对象的 toHaveMatchingId 匹配器下面是原文档给出的完整实现 —— 一个只依据id属性判断两个对象是否相等的匹配器expect.extend({ toHaveMatchingId(recieved, expected) { const pass recieved.id expected.id; if (pass) { return { pass: true, message: () expected id:${expected.id} to not match actual id:${recieved.id} }; } else { return { pass: false, message: () expected id:${expected.id} to match actual id:${recieved.id} }; } } });拆解这段代码可以提炼出自定义匹配器的三要素匹配器名称这里的toHaveMatchingId即匹配器名字。遵循 Jest 惯例自定义匹配器一般以to开头便于与内置匹配器保持一致的阅读体验。判定逻辑函数体内的pass计算即核心判定。本例仅比对recieved.id expected.id说明匹配器实现完全可以是普通的 JavaScript 运算没有任何黑魔法。两个返回分支pass: true与pass: false各返回一个带message函数的对象。注意pass: true分支的文案是期望 id:xxx 不匹配实际 id:xxx它服务于not.toHaveMatchingId()这类否定断言pass: false分支才是正常失败时展示的期望 id:xxx 匹配实际 id:xxx。另外参数名recieved、expected本身并无特殊约定你可以随意命名只要与函数体内的使用保持一致即可。在测试中像内置匹配器一样使用定义完成之后自定义匹配器在测试里的用法与内置匹配器完全一致直接链式调用即可test(compare objects, () { expect({ id: 001 }).toHaveMatchingId({ id: 001 }); // ✅ 通过 expect({ id: 001 }).toHaveMatchingId({ id: 002 }); // ❌ 失败输出expected id:002 to match actual id:001 });可以看到当断言失败时Jest 会自动使用我们在message()中构造的文案进行提示信息量比笼统的对象不相等要友好得多。原文档还附带了一个可以在线运行的示例环境读者可以边改边跑直观验证pass与message在不同输入下的表现。进阶让匹配器支持嵌套匹配自定义匹配器的判定逻辑虽然可以写任意 JavaScript 运算但直接使用或普通比较会丢失 Jest 的重要能力之一 ——嵌套匹配器asymmetric matchers。仓库中的另一篇 support-nested-matching-in-custom-jest-matchers 专门讲解了这个问题。考虑这样一个场景你想断言数组里包含某个满足条件的值如果只用some(value value containedValue)那么像下面这样使用expect.any(Number)就会失败尽管数组里确实有数字expect([a, 2, true]).toContainValue(expect.any(Number));解决方案是复用 Jest 内部使用的 Jasmine 比较工具equals它知道如何把原始值整数、布尔值乃至整个对象与嵌套的expect匹配器进行比对const { equals } require(expect/build/jasmineUtils); expect.extend({ toContainValue(receivedArray, containedValue) { const pass receivedArray.some(value equals(value, containedValue)); // return formatted pass/not-pass objects with messages return { ... } } });这也印证了自定义匹配器的本质是返回{ pass, message }的判定函数在判定逻辑内部你可以自由组合普通运算与 Jest 的嵌套匹配能力。如何全局注册在 setup 文件中配置如果项目里多个测试文件都要用到自定义匹配器逐个文件顶部调用expect.extend()显然不合适。仓库中的 configure-jest-to-run-a-test-setup-file 一篇提供了更优雅的集中式方案在package.json或jest.config.js里配置一个测试 setup 文件让 Jest 在每个测试运行前先加载它// package.json { // ... jest: { setupTestFrameworkScriptFile: rootDirsrc/setupTests.js } }其中setupTestFrameworkScriptFile指向以rootDir项目根目录为基准的 setup 文件路径。你只要把本文的expect.extend({ ... })调用放进src/setupTests.js所有测试文件就能直接使用toHaveMatchingId了。这类 setup 文件同样适用于需要全局配置测试框架适配器的场景如 Enzyme。相关 Jest 技巧速览围绕自定义匹配器 高效测试TIL 仓库的 JavaScript 分类下还有多篇可组合使用的笔记mock-a-function-with-return-values-using-jest用jest.fn()与mockReturnValue()/mockReturnValueOnce()控制 mock 函数的返回值适合在自定义匹配器的测试场景里隔离依赖。tell-jest-to-focus-on-running-only-one-test用test.only()聚焦单个测试块调试匹配器判定逻辑时非常有用。turn-off-console-error-messages-in-a-test临时用jest.fn()替换console.error在刻意测试失败路径时保持输出干净。test-coverage-stats-with-jest用jest --coverage查看语句/分支/函数/行覆盖率帮助你发现哪些断言包括自定义匹配器还缺少用例覆盖。小结自定义匹配器是 Jest 扩展断言能力的第一方入口。通过expect.extend()你可以用普通 JavaScript 逻辑定义领域专属的判定规则如按id比较对象严格遵守返回{ pass, message }的契约分别构造通过与否的提示文案在message()里嵌入实际值让失败信息具备可读性借助 setup 文件全局注册配合嵌套匹配器与equals工具实现更复杂的断言语义。当内置匹配器表达不了你的业务断言时写一个自定义匹配器往往比在测试里堆临时逻辑更清晰、更可复用。赞分享文档教程知识库【免费下载链接】til:memo: Today I Learned项目地址https://gitcode.com/gh_mirrors/ti/til点击查看免费下载相关推荐用 SCSS 插值Interpolation为 CSS 自定义属性赋值TIL 仓库中的 Sass 实战笔记用 SCSS 插值Interpolation为 CSS 自定义属性赋值TIL 仓库中的 Sass 实战笔记 CSS 自定义属性Custom Proper文档教程知识库如何让微信聊天记录成为你个人AI的成长养分如何让微信聊天记录成为你个人AI的成长养分 你是否想过那些与家人朋友的日常对话、工作的重要沟通、生活中的点滴分享不仅仅是简单的文字交流而是构建你个人AIjest-dom源码解析理解自定义匹配器的实现原理jest dom源码解析理解自定义匹配器的实现原理 jest dom是一个为Jest测试框架提供自定义DOM匹配器的强大工具库它让DOM元素的状态测试变得更测试前端开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考