资讯动态

three.js Editor 撤销/重做命令单元测试编写指南

发布时间:2026/9/10 1:36:44 来源:尧图企业网站定制
three.js Editor 撤销/重做命令单元测试编写指南【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js导读本文面向 three.js 内置编辑器editor/目录的二次开发者系统讲解如何为编辑器自定义的撤销/重做Undo/Redo命令编写单元测试。你将掌握从创建测试文件、注册命令与测试到编写断言与执行测试的完整流程同时通过源码理解editor.execute()、editor.undo()、editor.redo()背后的历史栈History机制确保新命令的撤销/重做行为可被自动化验证。概述为 Undo/Redo 命令写测试的核心思路three.js 编辑器中的所有可撤销操作都以“命令Command”的形式实现。编写单元测试的思路非常简单模拟一个场景执行动作然后执行撤销与重做并逐阶段校验结果。整个过程分为四个步骤创建一个新的单元测试文件将新命令与对应的测试文件注册进编辑器的测试套件编写测试用例执行 → 撤销 → 重做 → 断言在浏览器中执行测试并查看结果。下文将对每一步进行详细展开并结合仓库源码说明其背后的实现原理。1. 创建新的单元测试文件在路径test/unit/editor/TestDoSomethingCommand.js下创建新的测试文件。说明test/unit/editor/目录集中存放编辑器相关的测试文件DoSomethingCommand是占位名实际开发中请替换为你要测试的命令名例如TestMoveObjectCommand.js。从当前仓库的结构看editor/js/commands/目录见 editor/js/commands内置了 25 个可撤销命令如AddObjectCommand、MoveObjectCommand、SetPositionCommand、SetMaterialValueCommand等它们统一继承自 Command.js 基类并为后续编写测试提供了丰富的参考对象。2. 将新命令纳入编辑器测试套件打开编辑器测试套件入口test/unit/unittests_editor.html需要完成两处注册。2.1 注册命令对象类在文件内的!-- command object classes --注释块中加入新命令的脚本引用并保持字母序// !-- command object classes -- //... script src../../editor/js/commands/AddScriptCommand.js/script script src../../editor/js/commands/DoSomethingCommand.js/script // add this line script src../../editor/js/commands/MoveObjectCommand.js/script //...说明../../editor/js/commands/是从test/unit/出发指向仓库根目录下editor/js/commands/的相对路径。DoSomethingCommand.js是你自己的命令实现文件可参考 editor/js/commands 中现有命令的写法。2.2 注册测试文件在同一文件内的!-- Undo-Redo tests --注释块中加入刚才创建的测试文件引用// !-- Undo-Redo tests -- //... script srceditor/TestAddScriptCommand.js/script script srceditor/TestDoSomethingCommand.js/script // add this line script srceditor/TestMoveObjectCommand.js/script //...这里editor/是相对于测试套件所在目录的路径对应test/unit/editor/目录。同样建议尽量保持字母序排列。3. 编写测试用例3.1 推荐模板打开新创建的test/unit/editor/TestDoSomethingCommand.js粘贴以下骨架代码module( DoSomethingCommand ); test(Test DoSomethingCommand (Undo and Redo), function() { var editor new Editor(); var box aBox( Name your box ); // other available objects from CommonUtilities.js // var sphere aSphere( Name your sphere ); // var pointLight aPointLight( Name your pointLight ); // var perspectiveCamera aPerspectiveCamera( Name your perspectiveCamera ); // in most cases youll need to add the object to work with editor.execute( new AddObjectCommand( editor, box ) ); // your test begins here... } );模板要点解读module( DoSomethingCommand )与test(...)是 QUnit 测试框架的 API分别用于分组和定义单个用例var editor new Editor()在内存中创建编辑器实例测试完全在内存中运行不依赖浏览器 UIaBox()、aSphere()、aPointLight()、aPerspectiveCamera()等工厂函数来自CommonUtilities.js测试公共工具用于快速创建测试对象大多数命令需要先有一个可操作的对象因此先通过editor.execute( new AddObjectCommand( editor, box ) )把盒子加入场景。3.2 测试编写规范模板代码只是降低开发成本的起点你不必完全照搬。但测试至少应覆盖以下三个调用各一次editor.execute(...)—— 执行命令editor.undo()—— 撤销editor.redo()—— 重做。最佳实践对被测命令editor.execute( new DoSomethingCommand( {custom parameters} ) )连续调用两次。因为之后必然要执行一次撤销回退一步连续两次执行能为“撤销后”与“默认状态”之间提供可比较的自定义状态。尽量避免用ok()去断言默认值否则无法区分“撤销生效”与“从未执行”。3.3 断言节奏完整的断言分三个阶段进行执行后断言完成两次editor.execute()后做第一次断言确认两次执行都正确生效撤销后断言执行editor.undo()校验最后一步动作已被撤销重做后断言执行editor.redo()校验值恢复为执行后的期望状态。这一“执行 → 撤销 → 重做 → 各阶段断言”的模式正是对编辑器历史栈行为的直接验证。4. 执行测试在浏览器中打开编辑器的单元测试套件test/unit/unittests_editor.html测试框架会自动运行全部用例你可以在页面中查看每个模块module与用例test的通过/失败结果。源码深挖execute / undo / redo 的底层机制为了写出更精准的断言理解三个 API 在编辑器内部的实现很有帮助。Editor在 Editor.js 中把操作委托给历史管理器execute: function ( cmd, optionalName ) { this.history.execute( cmd, optionalName ); }, undo: function () { this.history.undo(); }, redo: function () { this.history.redo(); }真正的逻辑在 History.js 中其核心是两条栈this.undos与this.redos。execute入栈并执行execute( cmd, optionalName )的逻辑要点见 History.js若新命令与栈顶命令满足“可更新updatable”条件——即同为updatable、操作同一对象、同类型、同脚本、同属性——且时间差小于 500ms则调用lastCmd.update( cmd )合并更新而不是新压栈SetScriptValueCommand忽略时间限制否则将命令压入undos栈并分配自增的id随后调用cmd.execute()真正改变场景状态并清空redos栈新动作使既有重做记录失效。undo / redo弹栈并反转undo()见 History.js从undos弹出栈顶命令若该命令已序列化inMemory false则先fromJSON()恢复状态再调用cmd.undo()最后压入redosredo()见 History.js从redos弹出命令调用cmd.execute()重放并压回undos。这意味着命令本身必须实现成对的execute()与undo()方法才能保证撤销/重做对称。以内置的 MoveObjectCommand.js 为例execute()把对象从oldParent移动到newParent的指定索引undo()则反向把对象从newParent移回oldParent的原索引而 AddObjectCommand.js 中execute()调用editor.addObject()undo()调用editor.removeObject()互为逆操作。如果你的自定义命令需要序列化例如测试涉及toJSON/fromJSON注意Command基类见 Command.js已定义toJSON()输出type、id、name子类需在super.toJSON( this )基础上追加自身状态字段并在fromJSON()中恢复。总结为 three.js 编辑器的撤销/重做命令编写单元测试本质上就是围绕“执行 → 撤销 → 重做”三条调用构建断言循环在test/unit/editor/创建测试文件在test/unit/unittests_editor.html的两处注释块中注册命令与测试使用 QUnit 的module/test编写用例连续执行两次命令以获得可比较的自定义状态在浏览器中打开测试套件页面查看结果。理解 History.js 中undos/redos双栈的运作方式以及命令成对实现execute()/undo()的约定能帮助你写出覆盖边界场景、真正验证命令对称性的高质量测试。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价