关于 codeceptjs-fundamentals
面向 CodeceptJS 4 项目的测试编写技能,涵盖配置发现、场景组织、定位器和插件。适合维护 ESM/TypeScript 测试,并强调以用户视角编写可读场景。
主要功能
- 01检查 CodeceptJS 4 配置
- 02按用户场景组织测试
- 03使用语义定位器
- 04组织 Actor、页面对象与 Helper
- 05配置插件、断言和并行运行
适用场景
- 01为现有项目编写浏览器测试
- 02整理可复用的页面操作
- 03处理可选界面步骤与重试
- 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()
- 所有共享对象(actor、辅助工具、页面对象、支持对象)都注册到同一个容器中;
- 自定义辅助工具继承
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。
对于内置断言无法覆盖的情况,按以下优先顺序选择:
- 在辅助程序中编写可复用的自定义断言 —
I.seeTableIsOrdered('Price', 'desc');肯定断言命名为see*,否定断言命名为dontSee*;内部使用codeceptjs/assertions,不要直接使用throw new Error() - ExpectHelper(
@codeceptjs/expect-helper)— 在I上使用 chai 匹配器:I.expectEqual、I.expectDeepEqualExcluding、I.expectMatchesPattern、I.expectJsonSchema;与其他步骤一样显示在步骤日志中 - 直接使用
codeceptjs/assertions— 无依赖的工厂函数:equals(subject).assert(actual, expected)/.negate(...);失败信息的格式与I.see一致 - 对抓取的数据使用任意库(
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 })将配置/引导程序中的数据传入测试——可按名称注入
了解此项目
按顺序执行;跳过步骤会导致错误判断:
- 验证设置是否加载:
npx codeceptjs check -c <config>— 验证所有内容;输出也可作为清单。继续之前先修复失败项。 - 阅读当前生效的配置:辅助工具(包括浏览器/baseURL/视口/由环境变量驱动的值)、插件(包括
setCommonPlugins()注入的任何内容)、AI 提供方和所需的环境变量、环境选择机制、include中的页面对象、自定义辅助工具。 - 列出操作:
npx codeceptjs list -c <config>(--docs会添加 JSDoc;--action <name>用于查看单个操作)。存在自定义辅助工具时,实际的I.*接口与内置接口不同 — 建议使用某个方法前务必先检查。 - 列出测试:
npx codeceptjs dry-run -c <config>—--steps显示排队的操作,--grep用于筛选,--numbers提供与 MCPpauseAt匹配的每个测试步骤索引。- ⚠
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,然后停止