ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

bootstrap-datepicker 单元测试指南:基于 QUnit 的测试编写、运行与套件扩展

bootstrap-datepicker 单元测试指南:基于 QUnit 的测试编写、运行与套件扩展 bootstrap-datepicker 单元测试指南基于 QUnit 的测试编写、运行与套件扩展【免费下载链接】bootstrap-datepickerA datepicker for twitter bootstrap (twbs)项目地址: https://gitcode.com/gh_mirrors/bo/bootstrap-datepicker本篇技术指南以 bootstrap-datepicker 仓库中的 tests/README.md 为核心系统讲解该日期选择器插件的 QUnit 单元测试体系如何在浏览器中一键运行全部测试、如何通过 Grunt 命令行完成代码检查 测试的完整流程以及如何按照仓库约定新增测试模块、处理闰年等年份特例用例。读完本文你将掌握这套测试体系的目录规范、辅助工具与典型断言模式能够独立为插件的新功能或 bug 修复补充可靠的回归测试。测试体系概览单元测试在这个仓库中的角色根据 tests/README.md 的说明bootstrap-datepicker 使用 QUnit 编写单元测试其目标非常明确暴露 bug通过测试用例把潜在缺陷暴露出来便于修复squashing防止 bug 复生已修复的问题被固化为测试杜绝回归prevent bugs from respawning抑制新 bug在添加新特性、修改既有代码时用测试把行为边界钉死避免引入新的问题。这是一套标准的测试即契约做法测试用例不仅是验证工具更是对插件公开 API 行为的活的文档。仓库内所有测试均放置在tests/目录下并以 QUnit 的module模块概念组织测试套件——每个 JavaScript 文件对应一个模块。快速开始运行测试的两种方式方式一浏览器直接打开测试页最快捷的途径是用浏览器打开 tests/tests.html测试套件会自动运行并在页面上呈现结果。QUnit 测试页加载后会自动执行#qunit-tests中注册的全部用例并在页面顶部的 banner 区给出通过/失败统计。这种方式零依赖、无需构建适合在开发过程中快速验证某个改动是否破坏了既有行为。方式二命令行执行grunt test文档建议在命令行运行测试前先执行 jshint 和 jscs 代码检查详见下文grunt test实际上已内置这一步骤。安装依赖后在仓库任意位置执行$ grunt test该命令的实际定义在 Gruntfile.js 中grunt.registerTask(test, Lint files and run unit tests, [lint-js, /*lint-css,*/ qunit-all]);它由两个阶段组成lint-jsGruntfile.js依次运行jshint与jscs对js/bootstrap-datepicker.js、js/locales/*.js和Gruntfile.js做语法与代码风格校验检查规则分别来自js/.jshintrc、js/.jscsrc与grunt/.jshintrcqunit-allGruntfile.js运行qunit:main即tests/tests.html与qunit-timezone即tests/timezone.html。其中qunit-timezoneGruntfile.js会在运行前强制设置process.env.TZ Europe/Moscow再执行时区相关的测试——这是为了在固定时区下验证日期标题等与时区相关的渲染行为避免测试结果随运行机器时区漂移。此外package.json 将 npm 脚本test直接映射为grunt test因此也可以使用$ npm test一个容易被忽略的测试页timezone.html除了主测试页 tests/tests.html仓库还维护了独立的 tests/timezone.html。它只加载suites/timezone.js一个套件用于在固定时区Europe/Moscow下验证日期选择器标题渲染例如 tests/suites/timezone.js 中断言 2015 年 8 月视图的标题为August 2015。由于时区测试结果依赖进程环境变量它与主测试页解耦由grunt test统一串联执行。深入 tests.html测试运行器的组装结构tests/tests.html 是整个测试体系的入口装配文件其结构直接决定了测试的运行环境理解它有助于定位各类问题QUnit 本体assets/qunit.css与assets/qunit.js可选的调试日志assets/qunit-logging.js被注释掉注释说明console.log for test failures: enable locally if you need extra debug info本地排查失败用例时可临时启用被测对象../node_modules/jquery/dist/jquery.slim.jsjQuery slim 版与../js/bootstrap-datepicker.js插件本体专门的语言包../js/locales/bootstrap-datepicker.zh-CN.js注释说明它被加载是为了测试options.js中titleFormat的国际化用法测试辅助工具assets/utils.js与assets/mock.js详见下文测试套件清单位于 HTML 注释!-- Test suites --之后的一长串script srcsuites/...。页面 body 中按 QUnit 标准模板放置了#qunit-header、#qunit-banner、#qunit-testrunner-toolbar、#qunit-userAgent、#qunit-tests等容器以及一个关键的#qunit-fixture容器——这是 QUnit 提供的沙箱 DOM每个测试执行前会自动重置测试中创建的输入框、组件都会被挂载到它内部从而保证用例之间互不污染。页头还内联了一段样式把.datepicker容器绝对定位到屏幕之外top: -9999em; left: -9999em确保测试过程中弹出的日期选择器不会遮挡页面、也不会干扰测试流程。测试套件目录结构tests/suites 全解析按照 tests/README.md 的约定测试文件都放在tests/suites/目录树中一个 JS 文件对应一个 QUnit 模块。当前仓库的套件清单均已在tests.html中注册如下测试文件模块名覆盖内容tests/suites/formats.jsFormats日期格式解析yyyy/yy、mm/m、dd/d、MM/M、DD/D及相对日期1d、tomorrow、-1w、1y等tests/suites/mouse_navigation/all.jsMouse Navigation (All)鼠标交互通用行为点击面板不关闭、点击外部关闭tests/suites/mouse_navigation/2012.js / 2011.jsMouse Navigation 2011/2012指定年份下的鼠标导航特例跨年、跨月选择等tests/suites/keyboard_navigation/all.jsKeyboard Navigation (All)键盘通用行为TAB 关闭、配合daysOfWeekDisabled/datesDisabled的方向键导航tests/suites/keyboard_navigation/2012.js / 2011.jsKeyboard Navigation 2011/2012指定年份下的键盘导航特例tests/suites/touch_navigation/all.jsTouch Navigation (All)触摸交互touchstart点击外部隐藏面板tests/suites/component.jsComponent组件模式input add-on 图标的初始化、激活、禁用与导航tests/suites/events.jsEvents on initialization/Events事件行为初始化不触发change/changeDate视图切换触发changeYear等tests/suites/options.jsOptions全部配置项行为autoclose、startView、自定义format函数等tests/suites/inline.jsInline内联模式从data-date属性取初值、初始化即可见tests/suites/calendar-weeks.jsCalendar WeekscalendarWeeks选项周数列头与每行周数单元格tests/suites/data-api.jsDATA-APIdata-providedatepicker声明式初始化input、组件、按钮、日期区间tests/suites/noconflict.jsNoConflict$.fn.datepicker.noConflict()命名空间让渡tests/suites/methods.jsMethods实例方法remove/show/hide/update的可链式调用与状态变更tests/suites/methods_jquery.jsMethods (jQuery)jQuery 集合级方法调用及返回值语义年份特例的目录约定文档特别指出如果测试包含大量年份相关用例例如闰年与非闰年行为不同、某一年存在特定的 bug 行为应当把模块放进独立的年份目录tests/suites/new module/year.js其中new module是描述性模块名year是对应的四位年份。仓库中 tests/suites/mouse_navigation/2011.js 与 tests/suites/keyboard_navigation/2012.js 即遵循此约定其文件头部注释还记录了该年份的日历事实如 March 1, 2011 was on a Tuesday方便后续维护者理解用例意图。测试辅助工具utils.js 与 mock.js两个辅助文件为测试提供了关键的确定性保障tests/assets/utils.jsUTCDate(...)基于Date.UTC构造日期彻底消除本地时区对日期比较的干扰测试中所有期望日期均用它生成例如UTCDate(2012, 2, 15)表示 2012 年 3 月 15 日format_date(date)把日期格式化为YYYY-MM-DD HH:MM:SS.mmm的可读字符串datesEqual(actual, expected, message)调用QUnit.push(QUnit.equiv(...))对实际值与期望值做深度等价比较失败时会用format_date输出可读的差异信息。这是各套件中最常用的断言函数。tests/assets/mock.jspatch_date(f)临时替换全局Date构造器把Date.now固定为某个确定时间点测试运行结束后恢复原生Date。tests/suites/formats.js 中测试1d明天时正是用它把今天钉死在 2012 年 3 月 15 日从而断言结果为16-03-2012——没有这种打桩相对日期测试会随真实日期漂移而永远不稳定patch_show_hide(f)临时替换$.fn.show/$.fn.hide在调用时为元素添加/移除foo类用于断言show/hide 确实被调用过。编写新测试的完整流程结合 tests/README.md 的指引与仓库内真实用例新增测试需要三个步骤第一步新建模块文件如果新用例无法归入现有模块就在tests/suites/下新建一个模块文件tests/suites/new module.jsnew module应当是宽泛而有描述性的名字如Options、Methods而不是某一个具体用例名。若用例与特定年份强相关则遵循上文约定放入tests/suites/new module/year.js。第二步按 QUnit 标准写法组织用例每个模块文件以module(...)开头用setup/teardown管理生命周期用test(...)声明用例。以 tests/suites/methods.js 的骨架为例module(Methods, { setup: function(){ this.input $(input typetext value31-03-2011) .appendTo(#qunit-fixture) .datepicker({format: dd-mm-yyyy}); this.dp this.input.data(datepicker); this.picker this.dp.picker; }, teardown: function(){ this.dp.remove(); } });几个从源码中归纳出的关键写法DOM 挂到#qunit-fixture所有临时元素都appendTo(#qunit-fixture)QUnit 会在每个用例前自动清空该容器如 tests/suites/component.js 还演示了如何对 fixture 做事件监听与解绑通过data(datepicker)获取实例插件初始化后会把实例存入 jQuery data测试通过this.input.data(datepicker)拿到dp进而访问dp.picker面板 DOM、dp.viewDate当前视图日期、dp.dates已选日期数组等内部状态teardown 必须清理移除面板this.picker.remove()或调用dp.remove()避免用例间残留 DOM 与事件断言日期用datesEqual比较日期对象一律使用辅助函数而非裸equal以获得稳定的时区与格式表现。第三步导入 tests.html新文件只有被导入 tests/tests.html 才会被执行。在 HTML 注释!-- Test suites --之后的脚本列表中追加一行script srcsuites/new module.js/script若新增的是年份目录则为suites/new module/year.js。完成导入后浏览器刷新测试页或重新执行grunt test即可看到新用例运行。典型测试模式示例格式解析测试tests/suites/formats.js该套件系统验证了每个格式标记的解析与补零行为例如d日无前导零与dd日带前导零test(dd: Day of month, leading zero., function(){ this.input .val(2012-03-5) .datepicker({format: yyyy-mm-dd}) .datepicker(setValue); equal(this.input.val().split(-)[2], 05); });它还覆盖了一批颇具价值的回归用例如dd-mm-yyyy格式下的月份溢出防护Mar 31 解析后不得变成 Mar 01、闰日29-02-2012、yyyy-MM-dd下用数字解析月份的无限循环回归以及assumeNearbyYear选项对两位年份的世纪归属推断。这类用例直接印证了 js/bootstrap-datepicker.js 中日期解析器的边界行为。交互导航测试mouse / keyboard / touch三套导航测试共享相似骨架构造 input →.focus()激活面板 → 触发事件 → 用datesEqual断言dp.viewDate。例如 tests/suites/keyboard_navigation/all.js 验证在禁用周末setDaysOfWeekDisabled(0,6)后按左箭头视图日期会跳过禁用日跳到 3 月 1 日tests/suites/mouse_navigation/all.js 则验证点击面板不隐藏、点击面板外隐藏这一基础交互契约。DATA-API 测试tests/suites/data-api.js该套件逐项验证data-providedatepicker在不同宿主元素裸 input、input-append/input-prepend 组件、按钮、input-daterange 区间上通过focus或click事件即可完成声明式初始化——这与插件文档中零 JS 代码接入的能力一一对应。测试要点与最佳实践小结从源码中可以归纳出这套测试体系的几条实践准则供编写新用例时遵循用固定时间锚点保证确定性凡涉及今天/明天/相对日期的用例务必通过patch_date固定Date.now否则用例在真实日期变化后会失效日期比较统一走datesEqual避免时区与毫秒精度造成的误报teardown 中清理面板与实例每个模块的teardown都移除 picker 或调用dp.remove()确保#qunit-fixture干净可复用年份特例单独成目录闰年等年份相关行为按tests/suites/module/year.js组织便于追溯与维护新套件必须注册进 tests.html否则测试不会被任何运行入口加载提交前跑完整流水线grunt test会先执行 jshint/jscs 再运行全部 QUnit 用例含固定时区的 timezone 套件这是本仓库推荐的完整验证方式。通过这套规范bootstrap-datepicker 得以在迭代新功能的同时持续守住既有行为的边界——对希望为插件贡献代码或深入理解其行为的开发者而言tests/目录本身就是一份极具参考价值的实现文档。【免费下载链接】bootstrap-datepickerA datepicker for twitter bootstrap (twbs)项目地址: https://gitcode.com/gh_mirrors/bo/bootstrap-datepicker创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表