mcpField
C
codeceptjs-fundamentals

codeceptjs-fundamentals

github.com/codeceptjs测试与质量2116 次安装1 周前更新
代码仓库

关于 codeceptjs-fundamentals

面向 CodeceptJS 4 项目的测试编写技能,涵盖配置发现、场景组织、定位器和插件。适合维护 ESM/TypeScript 测试,并强调以用户视角编写可读场景。

主要功能

  1. 01检查 CodeceptJS 4 配置
  2. 02按用户场景组织测试
  3. 03使用语义定位器
  4. 04组织 Actor、页面对象与 Helper
  5. 05配置插件、断言和并行运行

适用场景

  1. 01为现有项目编写浏览器测试
  2. 02整理可复用的页面操作
  3. 03处理可选界面步骤与重试
  4. 04排查失败测试并收集诊断信息

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/codeceptjs/skills --skill codeceptjs-fundamentals

安装后在 Claude Code 中输入/codeceptjs-fundamentals确认已加载。

详细介绍

CodeceptJS 基础

分两步完成,依次进行:先了解以下规则,再查看当前项目的配置。

准入条件

  • CodeceptJS 4 仅支持 ESM/TypeScript;测试、配置、页面对象和辅助工具均使用 import/export。
  • package.json 中没有 "type": "module" → 先添加,再进行其他操作。
  • TypeScript:配置文件为 codecept.conf.ts,并在 require: [...] 中添加 TS 加载器入口。
  • 项目使用 3.x 或 CommonJS(require()、已移除的插件(如 autoLogin)、辅助工具 Nightmare)→ 停止操作,建议使用 migrate-codeceptjs-4。不要逐个修补文件——迁移需要针对整个项目进行。

主要规则

  • 测试应从用户视角编写:以线性场景描述操作,读起来像一段文字。
    • 好的示例:I.click('Login')、I.fillField('Email', ...)、I.see('Welcome')
  • 测试采用声明式写法,辅助工具采用命令式写法——建议按此分层:
    • 场景通过 I.* 描述用户做什么;实现细节放在更底层
    • 在 Scenario 中直接进行底层访问(this.helpers['Playwright'].page、fetch、文件系统)也可行,但建议将其移入辅助工具,并只暴露一个 I.* 操作
  • 保持测试简短:重复序列 → actor 方法、页面对象或步骤对象。
  • 优先使用语义定位器而非选择器,这样测试在标记语言频繁变更时仍能正常运行。

各项内容的放置位置(建议)

  • 全站操作(login、下拉菜单、富文本编辑器)→ actor 文件(自定义步骤)
  • 页面/屏幕操作和定位器 → 页面对象;SPA 屏幕 = 一个页面对象
  • 全站组件(导航、模态框、日期选择器)→ 页面片段/组件对象
  • 底层驱动访问(数据库连接、电子邮件、文件系统、复杂鼠标操作)→ 辅助工具
  • 通过 API 创建/清理数据 → 数据对象(REST/GraphQL 辅助工具 + _after() 清理),或 ApiDataFactory(I.have(...))
  • 不要过度设计:只有在多个测试中复用某种抽象时,才创建页面对象。

快捷做法

  • 需要登录 → 使用 autoLogin 插件或 actor 方法,而不是在每个测试中内联步骤
  • 测试数据 → 在测试前通过 API 创建,而不是通过 UI 创建
  • 测试过长 → 拆分成多个测试;长测试容易出错且难以理解
  • 可选 UI 元素/条件流程 → 使用 await tryTo(...),而不是 if (await I.grab...)——让场景保持线性

架构

  • 配置:仓库根目录下的 codecept.conf.{js,ts,mjs,cjs};使用 --config <file> 选择多个配置文件。
  • 辅助工具负责执行;I 负责委派。 每个 I.<method> 都会路由到实现该方法的活动辅助工具(Playwright、WebDriver、Puppeteer、Appium 共用一个 API 表面)。活动辅助工具 = helpers 下的各个键。测试调用 actor,而不是引擎——因此可以灵活切换后端。
  • DI 容器——存在的原因:
    • 所有共享对象(actor、辅助工具、页面对象、支持对象)都注册到同一个容器中;include 将名称映射到模块
    • 类由容器自动实例化——无需 new,也无需手动装配
    • inject() 返回延迟代理:在模块顶部解构时,会在调用时解析,因此循环引用页面对象也能正常工作;如果使用普通 import,则会得到 undefined
    • 访问方式:在 Scenario 签名中解构(Scenario('...', ({ I, loginPage }) => ...)),或在每个文件中执行一次 const { I } = inject()
  • 自定义辅助工具继承 Helper,在 helpers 下注册,并添加新的 I.* 方法。
    • 辅助工具内部不存在 I。 通过 this.helpers['<HelperName>'] 组合调用(例如 this.helpers['Playwright'].page、this.helpers['REST'].sendGetRequest(...))。
  • 插件是生命周期事件(suite.*、test.*、step.*、hook.*、multiple.*)的监听器。完整列表:node_modules/codeceptjs/lib/event.js。在 plugins 下注册,并设置 enabled: true。

配置变更陷阱

  • @codeceptjs/configure 会在加载时修改解析后的配置(setHeadlessWhen、setBrowser 等)。静态值可能不准确——在信任 show: / browser: 字段前,先搜索其导入位置。
  • setCommonPlugins():启用 retryFailedStep + screenshot;注册(未启用,除非使用 -p)pause、browser、aiTrace、heal。

值得了解的插件

  • retryFailedStep — 重试临时步骤失败
  • screenshot — 失败时截图;slides: true → output/records.html 幻灯片
  • pageInfo — 失败时转储 URL/HTML/控制台信息
  • auth — 复用登录会话(参见 codeceptjs-auth 技能)
  • aiTrace — 为 AI 调试记录每个步骤的截图/HTML/ARIA/控制台信息
  • pause — 交互式暂停
  • heal — 为失效步骤提供 AI 建议的修复(在 --debug 中关闭)
  • screencast — 运行过程的视频
  • customLocator — 将 $name 前缀映射到团队的测试属性(data-testid、data-qa)
  • browser — 仅限 CLI 的浏览器辅助配置覆盖(见下文)

注意:在 4.x 中,tryTo、retryTo、eachElement 不是插件——请从 codeceptjs/effects 导入。

从 CLI 使用插件

任何插件都可以通过 -p <plugin> 按运行启用或重新配置,参数用 : 串联:

npx codeceptjs run -p aiTrace                    # enable for this run
npx codeceptjs run -p screenshot:on=step         # reconfigure inline
npx codeceptjs run -p pause:on=file:path=tests/login_test.js;line=43
  • screenshot、pause、aiTrace、heal 共用 on= 触发条件:fail(默认值,aiTrace 除外)| step | test | file:path=...;line=N | url:pattern=<glob>
  • browser 插件可覆盖配置而不修改它——适用于 CI 矩阵任务、一次性环境变体:
    • -p browser:hide / -p browser:show / -p browser:browser=firefox / -p browser:windowSize=1280x800
    • 需要 @codeceptjs/configure

效果(codeceptjs/effects)

从 codeceptjs/effects 导入的流程控制函数。在 4.x 中,这些不再是插件或全局变量。

  • tryTo(() => ...) — 运行可能失败但不会中止测试的步骤;返回 boolean。
    • 优先使用 tryTo,而非 if: 场景应保持线性——不要根据获取到的值进行分支,以判断某种 UI 状态是否存在,而是尝试执行可选步骤,并根据布尔结果进行分支:
      const banner = await tryTo(() => { I.see('Cookie banner'); I.click('Accept cookies') })
      if (!banner) I.say('No cookie banner')
      
    • tryTo 块内会禁用自动重试。
  • retryTo(() => ..., maxTries, pollInterval = 200) — 重试步骤块,直到成功(适用于不稳定的元素、动画);回调会收到当前尝试次数。
  • hopeThat(() => ...) — 软断言(参见断言);最后调用 hopeThat.noErrors()。
  • within(locator | { frame }, fn) — 将解析范围限定到子树或 iframe;可以返回值(await)。尽可能优先使用单个操作的上下文参数(I.click('Save', '.toolbar'))——仅在确实需要限定范围的代码块中使用 within。

所有效果都会返回 Promise——请对其使用 await。

基于元素的 API(codeceptjs/els)

混合风格:结合使用 I.* 和直接元素访问。导入 { element, eachElement, expectElement, expectAnyElement, expectAllElements } from 'codeceptjs/els'。

  • element(locator, async el => { ... }) — 限定范围内访问单个元素;将 el.$(locator) 链接到子元素,无需重新查询
  • eachElement(locator, async (el, index) => ...) — 遍历元素集合
  • expectElement / expectAnyElement / expectAllElements(locator, fn) — 自定义条件
  • 元素是 WebElement 包装器——所有辅助方法都使用相同的 API:getText()、getAttribute()、isVisible()、isEnabled()、getBoundingBox()、exists()、$$()
  • 可选的用途说明字符串有助于改进调试日志:element('verify discount applied', '.price', ...)
  • 当内置方法无法满足需求时使用:元素集合、布局检查(getBoundingBox)、逐元素循环、在单个元素上链接操作。其他情况下优先使用 I.*,以提高可读性。

编写测试

  • 结构:每个文件包含一个 Feature(...),其中包含一个或多个 Scenario(...)。不要嵌套测试套件,也不要在一个文件中放置多个 Feature。
  • 钩子:Before、After、BeforeSuite、AfterSuite、Fail(...)。
  • 页面对象生命周期钩子:_before()(惰性执行,每个测试仅一次,在首次使用时执行)、_after()(未使用时跳过)、_beforeSuite()、_afterSuite()。
  • 以下情况必须使用 await:grab* 方法、导入的函数、包含异步操作的页面对象方法(其他情况会产生未处理的拒绝)。普通操作步骤不需要使用 await——记录器会串联执行它们。
  • 密钥:I.fillField('Password', secret(process.env.PASSWORD)) — 对日志、跟踪记录和 AI 提示词进行遮蔽。请从 codeceptjs 导入。
  • 会话:session(name, fn) — 为多用户 Scenario(聊天、多租户)创建并行浏览器上下文。

定位器

  • ARIA 定位器最可靠——不易受 CSS 重构影响,并能描述用户看到的内容:
    • I.click({ role: 'button', name: 'Save' })
  • 操作可接受普通字符串(可见文本、标签、占位符、name、aria-label)或对象({ css }、{ xpath }、{ id })。
  • 普通字符串已能匹配 aria-label——无需添加 'aria-label=...' 前缀。
  • 将上下文作为最后一个参数传入——范围限定的语义定位器优于冗长且未限定范围的定位器:
    • 使用 I.click('Save', '.toolbar'),而非 I.click('#toolbar .btn-save')
  • 避免使用基于样式的类名(.bg-green);优先使用语义化类名(.btn-save)。
  • 对使用 data-testid/data-qa 的应用,启用 customLocator,并使用 $name。
  • 没有合适的语义名称时,使用 locate(...) 构建器(.withClass、.withText、.inside、.and):locate('.button').withText('Click me')。

等待

  • 操作步骤会自动等待元素存在且可交互。仅当条件与交互无关时(例如网络调用后出现模态框、微调器消失),才添加显式的 waitFor*。
  • 避免使用 I.wait(N)——仅将其作为最后手段。

断言

优先使用内置浏览器断言:I.see、I.seeTextEquals、I.seeElement、I.seeInField、I.seeNumberOfElements、I.seeInCurrentUrl(以及对应的 dontSee* 方法)。失败信息清晰,并集成到记录器中。see 匹配可见文本;隐藏的 DOM 内容需要使用 seeInSource / seeElementInDOM。

对于内置断言无法覆盖的情况,按以下优先顺序选择:

  1. 在辅助程序中编写可复用的自定义断言 — I.seeTableIsOrdered('Price', 'desc');肯定断言命名为 see*,否定断言命名为 dontSee*;内部使用 codeceptjs/assertions,不要直接使用 throw new Error()
  2. ExpectHelper(@codeceptjs/expect-helper)— 在 I 上使用 chai 匹配器:I.expectEqual、I.expectDeepEqualExcluding、I.expectMatchesPattern、I.expectJsonSchema;与其他步骤一样显示在步骤日志中
  3. 直接使用 codeceptjs/assertions — 无依赖的工厂函数:equals(subject).assert(actual, expected) / .negate(...);失败信息的格式与 I.see 一致
  4. 对抓取的数据使用任意库(grab* 始终需要 await)— chai/jest/node:assert;测试会失败,但不会显示为步骤

软断言:使用 codeceptjs/effects 中的 hopeThat(() => I.see(...))——记录每个失败并继续执行;最后使用 hopeThat.noErrors(),若有任何记录的错误则令测试失败。

并行运行

  • run-workers <N> — 将 Scenario 分配到多个工作线程中
  • run-multiple <profile> — 通过配置中的 multiple 块使用配置档(浏览器、视口)

配置组织(推荐)

  • 为每个环境准备多个配置文件(codecept.conf.js、codecept.ci.conf.js 等);通过 config/ 目录中的模块共享部分配置
  • 使用 .env 文件和 dotenv 管理密钥及环境特定的值
  • 将导出的映射展开到 include 中,批量注册页面对象和组件
  • 使用 codeceptjs.container.append({ testUser }) 将配置/引导程序中的数据传入测试——可按名称注入

了解此项目

按顺序执行;跳过步骤会导致错误判断:

  1. 验证设置是否加载:npx codeceptjs check -c <config> — 验证所有内容;输出也可作为清单。继续之前先修复失败项。
  2. 阅读当前生效的配置:辅助工具(包括浏览器/baseURL/视口/由环境变量驱动的值)、插件(包括 setCommonPlugins() 注入的任何内容)、AI 提供方和所需的环境变量、环境选择机制、include 中的页面对象、自定义辅助工具。
  3. 列出操作:npx codeceptjs list -c <config>(--docs 会添加 JSDoc;--action <name> 用于查看单个操作)。存在自定义辅助工具时,实际的 I.* 接口与内置接口不同 — 建议使用某个方法前务必先检查。
  4. 列出测试:npx codeceptjs dry-run -c <config> — --steps 显示排队的操作,--grep 用于筛选,--numbers 提供与 MCP pauseAt 匹配的每个测试步骤索引。
    • ⚠ dry-run --grep 和 run --grep 不会选择相同的测试(4.1.0):run --grep 匹配 Feature + Scenario,而 dry-run --grep 仅匹配 Scenario 标题。dry-run --grep 'Dialogs' 会列出 0 个测试,而 run --grep 'Dialogs' 会执行全部 11 个测试。切勿根据 dry-run 的 grep 结果估算运行范围;如果必须精确选择整个 Feature,请通过文件路径指定(run tests/foo_test.ts)。

Gherkin 项目:npx codeceptjs gherkin:steps -c <config>。

参考文档位于 node_modules/codeceptjs/docs/ 下 — 请查阅文档,不要猜测 API。

报告

简短的文字总结。必须包括:

  • 标明由环境变量驱动的值(process.env.BROWSER || 'chromium',而不仅仅是 'chromium')
  • 标明配置冲突(show: true 为静态设置,但同时存在 setHeadlessWhen(CI);已配置 auth,但缺少凭证环境变量)
  • 根目录下没有配置,且未引用 --config → 建议使用 npx codeceptjs init .,然后停止
  • 检测到 3.x/CommonJS → 建议使用 migrate-codeceptjs-4,然后停止

评论

支持 Markdown