测试开发工具【免费下载链接】cucumber-jsCucumber for JavaScript项目地址https://gitcode.com/gh_mirrors/cu/cucumber-js点击查看免费下载这篇指南以 cucumber-js 官方 FAQdocs/faq.md为核心骨架逐一剖析四个高频问题为什么箭头函数里访问不到 World、为什么步骤定义模式必须全局唯一、为什么没有BeforeFeature/AfterFeaturehook以及安装错误The from argument must be of type string的根因。文中每个结论都会结合仓库源码如 src/runtime/test_case_runner.ts、src/runtime/step_runner.ts、src/support_code_library_builder/index.ts给出实现级证据读完你不仅能避开这些经典陷阱还能理解 Cucumber 内部调用用户代码的真实机制。一、为什么 World 实例在 Hook 或 Step Definition 中不可用1. 症状与直接原因如果你在 step definition 或 hook 中通过this引用 World 实例却拿不到预期对象最常见的原因是你用了 ES6 箭头函数。// 这样写this 不会指向 World Given(I have {int} cucumbers in my belly, (cucumberCount) { console.log(this) // 不是 World })Cucumber 在调用你的 step definition 和 hook 函数时内部使用的是Function.prototype.apply把 World 对象作为thisArg传入而箭头函数没有自己的this绑定apply对它的this毫无影响MDN 在箭头函数文档的 call, apply and bind 一节中明确说明了这一点。2. 源码级验证调用链在 src/runtime/step_runner.ts 中清晰可见const data await runInTestCaseScope({ world }, async () UserCodeRunner.run({ argsArray: invocationData.parameters, fn: stepDefinition.code, thisArg: world, // ← World 作为 this 传给用户函数 timeoutInMilliseconds, }) )而 src/user_code_runner.ts 正是通过apply完成最终调用try { fnReturn fn.apply(thisArg, argsArray) } catch (e) { ... }World 实例本身由 src/runtime/test_case_runner.ts 在每个场景开始时创建并注入attach、log、link、parameters四个内建能力resetTestProgressData(): void { this.world new this.supportCodeLibrary.World({ attach: this.attachmentManager.create.bind(this.attachmentManager), log: this.attachmentManager.log.bind(this.attachmentManager), link: this.attachmentManager.link.bind(this.attachmentManager), parameters: structuredClone(this.worldParameters), } satisfies IWorldOptions) ... }结论很明确只要需要访问 World就使用普通function而非箭头函数。详细写法见 docs/support_files/step_definitions.md 与 docs/support_files/hooks.md。3. 升级方案v10.8.0 起可用world对象配合箭头函数如果你的项目运行在 v10.8.0 及以上版本官方在 docs/support_files/world.md 中给出了箭头函数的替代方案从cucumber/cucumber导出world对象在 step/hook 体内直接读写它const { Given, Then, world } require(cucumber/cucumber) Given(my color is {string}, (color) { world.color color }) Then(my color should not be red, () { if (world.color red) { throw new Error(Wrong Color); } });它的底层实现并不神秘仓库在 src/index.ts 中通过export { contextProxy as context, worldProxy as world } from ./runtime/scope导出worldProxy定义于 src/runtime/scope/test_case_scope.ts它基于 Node.js 的AsyncLocalStorage保存当前正在执行场景的 World再用 src/runtime/scope/make_proxy.ts 生成的Proxy将属性读写转发给真实的 World 实例。因为转发是在步骤执行的作用域内惰性求值的所以箭头函数也能拿到正确的 World。⚠️ 注意world只能在 step 或Before/After/BeforeStep/AfterStephook 体内调用在场景之外例如BeforeAll/AfterAll或顶层调用会抛出错误——test_case_scope.ts中的getWorld()在取不到 store 时会直接 throw。测试用例见 src/runtime/scope/test_case_scope_spec.ts。二、为什么定义模式必须全局唯一而不是只在 Given/When/Then 各自范围内唯一1. 设计意图追求通用、无歧义的领域语言FAQ 给出的核心理由是用相同的措辞表达不同的含义本身就是歧义的定义。BDD 的价值在于让业务人员与开发团队共享一份无歧义的通用语言ubiquitous language。如果同一个文本I have 42 cucumbers在Given和Then下指向两个不同的实现阅读 feature 文件的人就无法确定一个步骤到底做了什么。官方的实践建议如果Given与Then的模式过于相似试着给Then模式加上 should 等措辞例如把Then the response is X写成Then the response should be X让语义层面自然区分开。2. 源码级验证匹配阶段根本不区分 keyword从实现上看这种全局唯一不是运行时额外强加的校验而是匹配机制的自然结果所有定义进同一个集合在 src/support_code_library_builder/index.ts 中Given、When、Then以及无关键字前缀的defineStep注册时全部 push 进同一个this.stepDefinitionConfigs数组keyword只是记录元数据不参与匹配。匹配时筛选全部定义在测试用例组装阶段src/assemble/assemble_test_cases.ts 的makeSteps()会把所有能匹配该步骤文本的 definition 全部收集起来const stepDefinitions supportCodeLibrary.stepDefinitions.filter((stepDefinition) stepDefinition.matchesStepName(pickleStep.text) ) return { id: newId(), pickleStepId: pickleStep.id, stepDefinitionIds: stepDefinitions.map((stepDefinition) stepDefinition.id), ... }其中matchesStepName定义于 src/models/step_definition.ts它直接用 Cucumber Expression 或正则表达式去匹配步骤文本完全不看 keyword。多于一个匹配即判 AMBIGUOUS运行时在 src/runtime/test_case_runner.ts 中按stepDefinitionIds数量分派结果——0 个为UNDEFINED超过 1 个即为AMBIGUOUS} else if (stepDefinitions.length 1) { return { result: { status: TestStepResultStatus.AMBIGUOUS, ...歧义错误的呈现src/runtime/helpers.ts 的getAmbiguousStepException会以表格形式列出所有匹配的模式及其定义位置Multiple step definitions match:方便你定位冲突。相关行为在 src/runtime/test_case_runner_spec.ts 中有测试覆盖。三、为什么没有BeforeFeature和AfterFeaturehook1. 设计理念场景Scenario才是执行单元Cucumber 运行测试时收集的是场景并将其组装为测试用例feature 文件本身并不作为测试运行的参与对象。feature 只是承载场景的容器场景应当彼此独立、可单独运行不依赖其他场景的执行顺序或状态。正因如此官方只提供全局与场景两个层级的Before/Afterhook而不是 feature 层级。2. 源码级验证hook 只分三个层级在 src/support_code_library_builder/index.ts 中可以看到全部 hook 类型它们被划分为三组集合没有任何 feature 级别的集合Hook层级存放集合Before/After场景test casebeforeTestCaseHookDefinitionConfigs/afterTestCaseHookDefinitionConfigsBeforeStep/AfterStep步骤test stepbeforeTestStepHookDefinitionConfigs/afterTestStepHookDefinitionConfigsBeforeAll/AfterAll整个测试运行test runbeforeTestRunHookDefinitionConfigs/afterTestRunHookDefinitionConfigs场景级 hook 在 src/runtime/test_case_runner.ts 中按需执行Before按定义顺序执行After按逆序执行详见 docs/support_files/hooks.mdBeforeStep/AfterStep、BeforeAll/AfterAll则分别由步骤执行与测试运行阶段驱动。3. 替代方案用 tag 定位同一个 feature 的场景如果你确实需要对某个 feature 下的所有场景做统一的前置/收尾工作FAQ 的建议是尽量把 setup 下沉到场景级并保证操作幂等重复执行结果一致用 tag 把 hook 绑定到该 feature 的场景。给 feature 打上标签如login后场景会自动继承 feature 的标签于是const { Before, After } require(cucumber/cucumber); Before({ tags: login }, function () { // 对 feature 下所有场景生效 this.login(administrator); }); After({ tags: login }, function () { this.logout(); });tag 表达式支持foo and bar、foo or bar等组合还可以用Before(foo, ...)的简写形式具体语法见 docs/support_files/hooks.md。四、报错The from argument must be of type string. Received type undefined怎么办1. 问题定位这是无效安装的经典症状当你看到这个 Node.js 底层path.relative抛出的ERR_INVALID_ARG_TYPE错误时说明你正在运行的 Cucumber 无法定位到真正的安装实例。在新版Cucumber 中错误提示已经被替换为更直白的信息Youre calling functions (e.g. Given) on an instance of Cucumber that isnt running. This means you have an invalid installation, mostly likely due to: ...该提示的实现位于 src/support_code_library_builder/index.tsSupportCodeLibraryBuilder在初始化时把Given/When/Then/Before等注册方法包在一个Proxy里任何注册调用都会先经过checkInstall()——如果库的status仍是PENDING即没有进入运行阶段就抛出上述错误。2. 为什么 Cucumber 对单实例如此敏感与许多无状态库不同Cucumber 是有状态的你调用Given(...)、Before(...)等函数时它把注册信息保存在当前实例的内存里直到测试运行阶段使用。因此项目中一切与 Cucumber 交互的代码必须操作同一个实例。一旦出现多个副本多份node_modules/cucumber/cucumber你在一个实例上注册的定义另一个实例运行时根本看不到——表现为函数调用无效。容易踩坑的几种情况官方在 docs/installation.md 中有完整说明全局安装npm install -g cucumber/cucumber。Cucumber 需要作为项目的本地依赖安装。仓库里的 src/cli/install_validator.ts 会在检测到全局安装时输出警告。重复依赖你的项目依赖cucumber/cucumber同时另一个第三方包比如你抽离出去的共享步骤定义库也依赖了cucumber/cucumber且版本不同导致两份实例并存。这与 React 生态中invalid hook call的 duplicate React 问题非常相似。废弃包项目里还依赖着旧的cucumber包而非cucumber/cucumber新旧包混用。任何代码都应统一迁移到cucumber/cucumber参见 UPGRADING.md。本地链接开发共享库时使用npm link/yarn link也可能引入重复实例。3. 诊断与修复步骤第一步确认实例数量。在项目根目录运行npm why cucumber/cucumber如果输出中出现了位于node_modules/my-shared-steps-library/node_modules/cucumber/cucumber之类的嵌套路径就说明存在重复副本。例如cucumber/cucumber8.4.0 dev node_modules/cucumber/cucumber dev cucumber/cucumber8.4.0 from the root project cucumber/cucumber8.3.0 dev node_modules/my-shared-steps-library/node_modules/cucumber/cucumber dev cucumber/cucumber8.3.0 from my-shared-steps-library1.0.0第二步按场景修复。共享库场景把共享库里的cucumber/cucumber从普通dependencies改为peerDependencies同时保留为devDependencies让宿主项目只保留一份实例。无法控制第三方库用 npm 的overrides或 Yarn 的resolutions把版本强制收敛到同一个。本地链接场景从共享库目录执行npm link ../my-project/node_modules/cucumber/cucumber假设my-project是你的宿主项目且与库目录相邻把它链接到宿主项目已解析的那份实例上。4. 额外提示全局安装与实例冲突问题在开启 docs/debugging.md 中的调试模式时会有更详细的警告输出。老版本只暴露TypeError [ERR_INVALID_ARG_TYPE]: The from argument must be of type string...这类晦涩信息原因与修复方式和上面完全一致——遇到旧错误时直接按无效安装流程排查即可。总结围绕 cucumber-js 官方 FAQ 的四个经典问题可以提炼出几条底层规律World 通过apply(thisArg, argsArray)注入见 src/user_code_runner.ts所以箭头函数拿不到this需要箭头函数时v10.8.0 可使用world导出对象底层是AsyncLocalStorageProxy见 src/runtime/scope/test_case_scope.ts。步骤匹配不区分Given/When/Then所有模式放在同一集合、按文本匹配多个匹配即判AMBIGUOUS见 src/assemble/assemble_test_cases.ts 与 src/runtime/test_case_runner.ts因此模式必须全局唯一。没有 feature 级 hook 是刻意设计场景才是独立执行单元需要按 feature 批量处理时用 tag 表达式替代。The from argument must be of type string是无效安装的历史症状用npm why cucumber/cucumber定位重复实例再通过 peer dependency、overrides/resolutions 或正确的npm link收敛到单实例详见 docs/installation.md。理解这些内部机制不仅能帮你快速排除上述报错也能让你在编写 step definitions 与 hooks 时参考 docs/support_files/step_definitions.md 与 docs/support_files/hooks.md写出更健壮、更符合 Cucumber 设计哲学的测试代码。赞分享测试开发工具【免费下载链接】cucumber-jsCucumber for JavaScript项目地址https://gitcode.com/gh_mirrors/cu/cucumber-js点击查看免费下载相关推荐PHP-CS-Fixer 规则深度解析final_public_method_for_abstract_class 强制抽象类公共方法为 finalPHP CS Fixer 规则深度解析final_public_method_for_abstract_class 强制抽象类公共方法为 final 导读 f测试开发工具LocalAI 常见问题深度解析后端按需安装、模型管理与推理性能排查LocalAI 常见问题深度解析后端按需安装、模型管理与推理性能排查 LocalAI lo/LocalAI 是“开源 AI 引擎”一个核心二进制或容器人工智能大模型模型推理服务本地部署LLM 网关多模态AI AgentRAGMCP 服务Defender Control终极指南如何永久禁用Windows Defender完整教程Defender Control终极指南如何永久禁用Windows Defender完整教程 Defender Control是一款开源的Windows D应用安全上一篇Sabaki让围棋学习与分析变得高效专业的开源方案下一篇React Native Map Clustering事件处理完全教程onClusterPress与onRegionChangeComplete的实战应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考