news 2026/9/28 7:40:08

axe-core 测试模式指南:从 Commons 单元测试到规则集成测试的完整实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
axe-core 测试模式指南:从 Commons 单元测试到规则集成测试的完整实践
  • 测试

【免费下载链接】axe-core

Accessibility engine for automated Web UI testing

项目地址:https://gitcode.com/gh_mirrors/ax/axe-core
点击查看免费下载

本篇指南围绕 axe-core 官方文档 doc/examples/test-patterns.md 展开,系统梳理 axe-core(自动化 Web UI 无障碍测试引擎)的测试约定与分层架构:从 commons 工具函数、check evaluate 的单元测试,到基于 HTML + JSON 配对文件的规则集成测试、完整页面集成测试与虚拟规则测试。读完本文,你将掌握 axe-core 测试文件的组织位置、每种测试的固定写法、断言方式与常见陷阱,可以直接照着模式为新的规则或检查项补齐测试。

测试分层总览:单元测试与集成测试的分工

axe-core 的测试体系分为两大层:单元测试与集成测试。单元测试直接调用被测试函数(commons 工具函数或 check 的 evaluate 方法),不经过完整的规则引擎;集成测试则通过axe.run()或axe.runVirtualRule()走真实审计流程,验证规则在真实 DOM / 虚拟节点上的最终行为。

两者依赖同一套测试基础设施:test/testutils.js中的axe.testUtils命名空间。该文件在测试启动时被注入全局,提供了MockCheckContext、queryFixture、queryShadowFixture、getCheckEvaluate、checkSetup等核心方法,并为整个测试套件注册全局 mochabeforeEach/afterEach钩子(registerHooks),在每条用例前后重置 axe 状态并清空#fixture元素。编写任何测试前,建议先阅读 test/testutils.js 了解这些工具的行为。

单元测试一:Commons 工具函数

Commons 是 axe-core 对外暴露的公共工具库(如axe.commons.text、axe.commons.dom、axe.commons.color等),对这些纯函数编写单元测试时,直接使用 mocha 的describe/it与 chai 的assert断言即可,不需要 fixture 或虚拟节点。

官方文档给出的text.sanitize示例体现了 commons 测试的典型写法:

describe('text.sanitize', function () { it('should collapse whitespace and trim', function () { assert.equal(axe.commons.text.sanitize('\thi\t'), 'hi'); assert.equal(axe.commons.text.sanitize('\t\nhi \t'), 'hi'); assert.equal(axe.commons.text.sanitize('hello\u00A0there'), 'hello there'); }); it('should accept null', function () { assert.equal(axe.commons.text.sanitize(null), ''); }); });

要点:

  • 通过全局axe.commons.*直接访问工具函数,无需任何 DOM 准备;
  • 一个it内可包含多个assert.equal,覆盖不同输入(这里覆盖了制表符、换行、不间断空格\u00A0与null边界);
  • 测试文件通常放在 test/commons 目录下与源码一一对应的子目录中(如test/commons/text/),仓库中已有大量同类示例可参考。

单元测试二:Check 的 evaluate 方法

与 commons 不同,check 的 evaluate 方法需要真实的this上下文(用于收集data()、relatedNodes()、异步结果等),因此单元测试必须借助axe.testUtils.MockCheckContext()构造上下文,再通过axe.testUtils.getCheckEvaluate(checkId)拿到包装后的 evaluate 函数,用.call()绑定上下文调用。

官方文档以aria-allowed-attr为例:

describe('aria-allowed-attr', function () { const fixture = document.getElementById('fixture'); const checkContext = axe.testUtils.MockCheckContext(); afterEach(function () { checkContext.reset(); }); it('should return true if all ARIA attributes are allowed', function () { const vNode = queryFixture( '<div role="textbox" aria-placeholder="foo" id="target"></div>' ); assert.isTrue( axe.testUtils .getCheckEvaluate('aria-allowed-attr') .call(checkContext, vNode.actualNode, {}, vNode) ); }); });

这里有几层细节值得展开:

  • queryFixture(html):来自test/testutils.js的工具方法,它把 HTML 注入#fixture元素(testUtils.fixtureSetup),随后执行axe.setup()构建虚拟树,最后按默认选择器#target找到目标节点并返回其VirtualNode(在 test/testutils.js 中定义,找不到目标时断言失败并给出明确提示)。因此被测 HTML 片段中必须包含id="target"的元素。
  • MockCheckContext():返回一个模拟的 check 上下文对象,包含_data、_relatedNodes、_onAsync字段以及async()、data()、relatedNodes()、reset()方法(实现见 test/testutils.js)。每次用例结束后调用checkContext.reset()清空上一次的data、relatedNodes与异步回调,避免用例间相互污染。
  • getCheckEvaluate(checkId, testOptions):返回 evaluate 包装器,它会在调用前合并 check 的默认选项(check.getOptions(options)),并在调用后自动校验返回结果是否有对应的 pass / fail / incomplete 消息(从axe._audit.data.checks[checkId].messages中查证)。这意味着即使你的用例只关心返回值,消息完整性也会被隐式验证。对只在规则none数组中使用的 check,其消息键与结果相反(返回false时查pass消息),包装器会自动处理该语义。
  • 调用签名:.call(checkContext, vNode.actualNode, {}, vNode)三个参数分别是真实 DOM 节点、选项对象(这里为空{})、虚拟节点——这正是 check evaluate 在引擎内的标准调用签名(见 lib/checks/aria/aria-allowed-attr-evaluate.js 等实现)。

单元测试三:Shadow DOM 场景

当被测逻辑需要覆盖 Shadow DOM 内容时,使用queryShadowFixture同时准备**宿主(light DOM)与影子边界(shadow DOM)**两份 HTML:

it('should work with Shadow DOM', function () { const vNode = queryShadowFixture( '<div id="host"></div>', '<div role="button" id="target">Test</div>' ); // Test your function against the shadow DOM content });

queryShadowFixture(content, shadowContent, targetSelector)的实现要点(见 test/testutils.js):

  • 将content注入 fixture,在容器上调用attachShadow({ mode: 'open' }),再把shadowContent写入 shadow root;
  • 目标查询优先在 shadow root 内查找#target,找不到再回退到 light DOM;targetSelector也可传对象{ shadow, target }自定义两边的选择器;
  • 挂载完 shadow DOM 后才执行axe.setup(),确保扁平化树(composed tree)包含影子内容,返回的仍是目标节点的VirtualNode。

需要说明的是:随着浏览器对**声明式 Shadow DOM(declarative shadow DOM)**支持度的提升,test/testutils.js 中已通过正则/<template\s+shadowrootmode\s*=\s*(['"]?)open\1/自动检测<template shadowrootmode="open">片段,并在queryFixture、checkSetup中自动改用setHTMLUnsafe解析、按"从深到浅"顺序在多层 shadow root 中定位#target。因此新写的 Shadow DOM 用例推荐直接用声明式写法配合queryFixture,旧的queryShadowFixture/shadowCheckSetup在源码注释中已被标记为 deprecated。

集成测试:规则的 HTML + JSON 配对文件

规则(rule)级别的行为变更需要配套集成测试。官方文档明确了两类放置位置:

  • mocha 托管的规则测试:test/integration/rules/<rule-name>/目录,存放规则的 HTML 与 JSON 配对文件;
  • 完整 HTML 页面测试:test/integration/full/<rule-name>/目录,面向需要完整页面上下文的规则(如 landmark、页面级规则)。

文档以aria-allowed-attr为例,给出了 HTML 与 JSON 配对文件的格式。

HTML 文件(如aria-allowed-attr.html)

<div role="textbox" aria-placeholder="foo" id="pass1">Valid</div> <div role="button" aria-placeholder="invalid" id="fail1">Invalid</div>

实际仓库中,这一目录的命名约定更细:test/integration/rules/aria-allowed-attr/下拆分为passes.html、failures.html、incomplete.html三份 HTML(分别对应通过、违规、不完整三类预期),每份文件内部元素以pass0、pass1… 与fail1、fail2… 等 id 递增编号,测试框架按 JSON 中列出的 id 选择器逐一断言(可对照 test/integration/rules/aria-allowed-attr/passes.html 与 test/integration/rules/aria-allowed-attr/failures.json)。

JSON 文件(如aria-allowed-attr.json)

{ "description": "aria-allowed-attr test", "rule": "aria-allowed-attr", "violations": [["#fail1"]], "passes": [["#pass1"]] }

字段说明:

  • description:测试描述;
  • rule:被测规则 id,必须与lib/rules/<rule-name>.json中注册的规则 id 一致;
  • passes/violations:通过 / 违规元素的 id 数组;
  • 元素 id 使用axe selector 数组格式(数组的每个元素依次表示一层 DOM 边界)。

iframe 内元素的定位格式

当目标元素位于 iframe 内部时,选择器必须写出跨边界路径——先指向 iframe 元素本身,再指向其中的元素:

{ "violations": [["iframe", "#fail-inside-iframe"]] }

这是 axe selector 数组格式的典型应用:数组第 1 项是 iframe 的 CSS 选择器,第 2 项是 iframe 文档内的目标选择器,多层 iframe 依此类推。相关实现可参考test/testutils.js中的shadowQuerySelector与runPartialRecursive(后者会递归遍历各 frame 的上下文分别调用axe.runPartial)。

完整页面集成测试(full-page)

对 landmark 规则、页面级规则这类必须运行在完整 HTML 文档上的规则,test/integration/rules/<rule>/中注入片段的方式不够用,应改用test/integration/full/<rule-name>/。这些测试针对完整 HTML 文档运行axe.run(),而不是注入碎片。

仓库中的典型结构是每个场景一对*.html+*.js文件,例如 test/integration/full/landmark-banner-is-top-level/ 下同时存在landmark-banner-is-top-level-pass.html/-pass.js与landmark-banner-is-top-level-fail.html/-fail.js。JS 文件在before钩子中等待嵌套 frame 加载完成后执行axe.run,再分别断言 violations / passes / inapplicable / incomplete 的数量,例如 test/integration/full/landmark-banner-is-top-level/landmark-banner-is-top-level-pass.js:

describe('landmark-banner-is-top-level test pass', () => { let results; before(done => { axe.testUtils.awaitNestedLoad(() => { axe.run( { runOnly: { type: 'rule', values: ['landmark-banner-is-top-level'] } }, (err, r) => { assert.isNull(err); results = r; done(); } ); }); }); // ... });

关键工具是axe.testUtils.awaitNestedLoad()(test/testutils.js):它会等待当前文档readyState === 'complete',并递归等待所有嵌套 iframe 加载完成,再执行回调——对于含 frame 的页面级测试这是必需的等待手段。断言语义与规则集成测试一致:results.violations、results.passes[0].nodes、results.inapplicable、results.incomplete分别对应违规、通过、不适用、不完整四类结果。test/integration/full/aria-hidden-body/pass.js(test/integration/full/aria-hidden-body/pass.js)是另一个更精简的同类范例。

虚拟规则测试(virtual rules)

某些规则不需要真实 DOM,仅凭虚拟节点(serialized node data)即可运行,这类规则应在虚拟规则测试中验证其与axe.run()在序列化节点数据上的兼容性。

官方文档描述的目录为test/virtual-rules/;在当前仓库中,对应实现位于test/integration/virtual-rules/(内含 48 个*.js测试文件),并由 test/test-virtual-rules.js 通过 glob 批量加载运行。每个规则一个describe,核心 API 是axe.runVirtualRule(ruleId, vNodeData):

describe('aria-allowed-attr virtual-rule', () => { it('should pass for required attributes', () => { const results = axe.runVirtualRule('aria-allowed-attr', { nodeName: 'div', attributes: { role: 'checkbox', 'aria-checked': true } }); assert.lengthOf(results.passes, 1); assert.lengthOf(results.violations, 0); assert.lengthOf(results.incomplete, 0); }); // ... });

虚拟节点数据以nodeName+attributes描述元素(test/integration/virtual-rules/aria-allowed-attr.js)。完整的aria-allowed-attr虚拟规则测试覆盖了:必需属性通过、允许属性通过、非法(未注册)属性通过、无 role 时的全局属性通过、无 role 时的非全局属性违规、role 不允许的属性违规、隐式 role(<a href="#">)下的属性违规、通过axe.configure({ standards: { ariaAttrs: {...} } })注入 unsupported 属性后的违规、自定义元素(custom-elm1)上的非全局属性返回 incomplete,以及废弃属性(aria-grabbed)返回 incomplete、废弃属性叠加违规属性时判违规等边界场景。

虚拟规则测试的价值在于:它不依赖浏览器 DOM 渲染,直接以节点数据驱动完整审计流水线,能快速验证规则在"纯数据"形态下的判定逻辑,是补充真实 DOM 集成测试的高性价比手段。因此文档建议:为合适的规则新增或同步更新虚拟规则测试。

测试约定速查与最佳实践

综合官方文档与仓库实现,编写 axe-core 测试时可遵循以下约定:

  1. commons 函数测试:放在test/commons/对应子目录,直接用assert断言函数返回值,无需 DOM。
  2. check evaluate 测试:用MockCheckContext()管理上下文,queryFixture()注入 HTML 并定位#target,getCheckEvaluate(checkId).call(ctx, node, options, vNode)执行,afterEach中checkContext.reset()。
  3. Shadow DOM:优先用声明式<template shadowrootmode="open">配合queryFixture;需要传统写法时使用queryShadowFixture。
  4. 规则集成测试:在test/integration/rules/<rule>/下放置 HTML + JSON 配对文件(passes/failures/incomplete),id 用 axe selector 数组格式,iframe 内元素写["iframe", "#target"]。
  5. 页面级规则:放入test/integration/full/<rule>/,用awaitNestedLoad等待加载后用axe.run断言各类结果数量。
  6. 虚拟规则:在test/integration/virtual-rules/中为规则添加axe.runVirtualRule用例,覆盖 pass / violation / incomplete 三类结果。

这套模式覆盖了从最小单元到完整页面的全部测试层级。任何改动(尤其是规则与 check 行为变更)都应按上述约定补齐对应测试,并在本地跑通对应测试文件后再提交,以保持 axe-core 高覆盖率的测试传统。

  • 测试

【免费下载链接】axe-core

Accessibility engine for automated Web UI testing

项目地址:https://gitcode.com/gh_mirrors/ax/axe-core
点击查看免费下载

相关推荐

上一篇:碧蓝航线Alas脚本:智能自动化配置全攻略
下一篇:10分钟清出20GB:Mac上Czkawka磁盘清理实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 7:39:47

3步让打开浏览器直接进入网站,选哪家好别被坑

3步让打开浏览器直接进入网站,选哪家好别被坑 改个需求建站公司拖一周,这种憋屈事谁没经历过?你催了八遍,对方回复“正在排期”,转头又让你等三天。这时候你心里肯定在骂娘:这钱花得值吗?市面上建站公司哪家好?其实,对于独立站长或者中小企业主来说,很多时候你不需要外包,自己搭建一个能打开浏览器直接进入网站…

作者头像 李华
网站建设 2026/9/28 7:39:35

phpmysql网站开发项目式教程性能优化

不会代码也能做网站?PHP MySQL项目式教程图解步骤 想做个网站,打开招聘网站一看,全是PHP、MySQL、HTML,头都大了。自己完全不懂代码,看着那些复杂的后台配置和报错信息,心里直打鼓。别慌,这就是大多数新手卡在第一步的原因。 其实,只要搞懂 phpmysql网站开发项目式教程…

作者头像 李华
网站建设 2026/9/28 7:39:34

网站手机版绑定域名速查手册:防坑避雷实操指南

网站手机版绑定域名速查手册:防坑避雷实操指南 找建站公司最怕啥?不是技术不行,是怕被坑高价,尤其是涉及“网站手机版绑定域名”这种看似简单实则暗藏玄机的环节。很多老板觉得,不就是把手机网站挂个域名吗?结果一询价,几千块起步,还说不清楚具体干了啥。这份速查手册就是为了解决这个痛点,把那些藏在合同里的猫腻…

作者头像 李华
网站建设 2026/9/28 7:39:31

建站空间哪个好?选对服务器避开90%坑,附源码部署实战

建站空间哪个好?选对服务器避开90%坑,附源码部署实战 域名备案卡住、服务器配置选错,这大概是建站时最让人头大的两件事。很多老板找我们做站,第一句话就是“帮我找个空间”,结果发现所谓的“空间”根本跑不动业务。别被那些花里胡哨的名词忽悠了, 建站空间哪个好…

作者头像 李华
网站建设 2026/9/28 7:39:26

wordpress阿里云oss2026最新

WordPress配阿里云OSS一文搞懂,告别模板丑站 还在为网站加载慢、模板丑得掉渣而头疼?别硬扛了,今天咱们就 一文搞懂 WordPress 如何配合阿里云 OSS…

作者头像 李华
网站建设 2026/9/28 7:39:10

应用商城app下载安装避坑指南:拒绝被拖工期,安全上线只需3步

应用商城app下载安装避坑指南:拒绝被拖工期,安全上线只需3步 改个需求建站公司拖一周,这种憋屈事谁没遇到过?更糟心的是,好不容易上线的应用商城,因为下载入口不安全,用户装了恶意插件或者根本装不上,流量全白费。今天这篇 应用商城app下载安装 避坑指南,就是帮你把坑填平。…

作者头像 李华