如何将Asciidoctor(AsciiDoc)集成至Cypress并结合二者编写测试
完全可以实现,无需依赖第三方封装的专用集成插件。核心实现逻辑是通过Asciidoctor官方的JS解析库,读取结构化编写的AsciiDoc测试用例文件,将文档内的测试套件、单用例信息、前置条件、操作步骤、断言规则映射为Cypress原生的describe、it块及对应API调用,最终达成「一份AsciiDoc文件同时作为可阅读的测试文档、可直接执行的自动化测试脚本」的效果,非技术角色也能直接参与用例编写维护。
安装基础依赖
进入已初始化的Cypress项目根目录,执行以下命令安装解析所需依赖:npm install @asciidoctor/core --save-dev约定AsciiDoc测试用例写作规范
先统一用例的标记规则,避免解析逻辑混乱,推荐的最小可用格式示例如下:= 用户登录模块测试套件 :suite-tag: smoke == 正确账号密码登录成功 :case-priority: P0 :precondition: 访问页面路径 /login . 在用户名输入框输入内容 `test_user_01` . 在密码输入框输入内容 `Test@123456` . 点击id为`login-submit`的登录按钮 . 断言:页面路径跳转至 `/dashboard` . 断言:页面可见文本 `欢迎回来,test_user_01` == 错误密码登录提示失败 :case-priority: P1 :precondition: 访问页面路径 /login . 在用户名输入框输入内容 `test_user_01` . 在密码输入框输入内容 `wrong_pass` . 点击id为`login-submit`的登录按钮 . 断言:页面class为`error-tip`的元素可见文本 `账号或密码错误,请重试`规则说明:一级标题对应Cypress测试套件名,文档属性(
:key: value格式)存储测试标签、优先级、前置条件等元信息,二级标题对应单个测试用例名,有序列表对应逐行执行的测试步骤,带断言:前缀的步骤映射为Cypress断言逻辑,反引号包裹的内容为步骤对应的操作参数。编写AsciiDoc用例解析器
在Cypress的支持文件目录下新建asciidoc-loader.js,实现AsciiDoc AST到Cypress调用的映射逻辑,核心代码如下:const asciidoctor = require('@asciidoctor/core')() const path = require('path') const fs = require('fs') function loadAdocCases(caseDir = path.join(process.cwd(), 'cypress/e2e/adoc-cases')) { // 读取目录下所有.adoc后缀的用例文件 const caseFiles = fs.readdirSync(caseDir).filter(file => file.endsWith('.adoc')) caseFiles.forEach(file => { const fileContent = fs.readFileSync(path.join(caseDir, file), 'utf-8') const doc = asciidoctor.load(fileContent) const suiteName = doc.getTitle() // 生成Cypress测试套件 describe(suiteName, () => { // 遍历每个二级标题对应的单测试用例 doc.getSections().forEach(section => { const caseName = section.getTitle() const precondition = section.getAttribute('precondition') // 提取有序列表内的所有步骤 const stepList = section.getBlocks() .find(block => block.getContext() === 'olist') ?.getItems() ?.map(item => item.getText()) || [] // 生成单个Cypress测试用例 it(caseName, () => { // 执行前置操作 if (precondition?.startsWith('访问页面路径')) { const targetUrl = precondition.match(/`(.*?)`/)[1] cy.visit(targetUrl) } // 逐行解析执行步骤 stepList.forEach(step => { // 输入框操作映射 if (step.startsWith('在用户名输入框输入内容')) { const content = step.match(/`(.*?)`/)[1] cy.get('[data-testid="username-input"]').type(content) } if (step.startsWith('在密码输入框输入内容')) { const content = step.match(/`(.*?)`/)[1] cy.get('[data-testid="password-input"]').type(content) } // 点击操作映射 if (step.startsWith('点击id为')) { const eleId = step.match(/`(.*?)`/)[1] cy.get(`#${eleId}`).click() } // 路径断言映射 if (step.startsWith('断言:页面路径跳转至')) { const targetPath = step.match(/`(.*?)`/)[1] cy.location('pathname').should('eq', targetPath) } // 文本可见断言映射 if (step.startsWith('断言:页面可见文本')) { const text = step.match(/`(.*?)`/)[1] cy.contains(text).should('be.visible') } // 元素文本断言映射 if (step.startsWith('断言:页面class为')) { const [, className, expectedText] = step.match(/class为`(.*?)`的元素可见文本 `(.*?)`/) cy.get(`.${className}`).should('contain', expectedText) } }) }) }) }) }) } module.exports = { loadAdocCases }配置Cypress加载执行AsciiDoc用例
在Cypress的e2e测试目录下新建入口执行文件run-adoc-cases.cy.js,内容仅需引入解析器并触发加载即可:import { loadAdocCases } from '../support/asciidoc-loader' // 解析所有AsciiDoc用例并动态生成可执行测试 loadAdocCases()后续将写好的
.adoc格式用例放到cypress/e2e/adoc-cases目录下,正常启动Cypress运行测试即可,执行效果和原生JS/TS编写的用例完全一致,支持截图、录屏、测试报告等所有Cypress原生能力。
- 初期不需要覆盖所有操作类型,先把团队高频使用的操作、断言规则做进映射逻辑即可,后续按需扩展,比引入重量的BDD框架灵活度更高、维护成本更低。
- 可以给AsciiDoc用例增加自定义属性支持测试筛选、接口Mock、测试数据注入等能力,比如加
:mock: getUserInfo 200的属性,解析时自动触发cy.intercept逻辑。 - 调试解析逻辑时,可以先打印Asciidoctor解析出的AST结构,确认节点类型、属性、文本内容读取正确后再写映射规则,排查问题效率更高。
内容的提问来源于stack exchange,提问作者DMate

