基于Virtual DOM与Diff DOM的测试代码生成

news/2024/7/2 23:55:41

尽管是在年末,并且也还没把书翻译完,也还没写完书的第一稿。但是,我还是觉得这是一个非常不错的话题——测试代码生成。

当我们在写一些UI测试的时候,我们总需要到浏览器去看一下一些DOM的变化。比如,我们点击了某个下拉菜单,会有另外一个联动的下拉菜单发生了变化。而如果这个事件更复杂的时候,有时我们可能就很难观察出来他们之间的变化。

Virtual DOM

尽管这里的例子是以Jasmine作为例子,但是我想对于React也会有同样的方法。

一个Jasmine jQuery测试

如下是一个简单的Jamine jQuery的测试示例:

  describe("toHaveCss", function (){beforeEach(function (){setFixtures(sandbox())})it("should pass if the element has matching css", function (){$("#sandbox").css("display", "none")$("#sandbox").css("margin-left", "10px")expect($("#sandbox")).toHaveCss({display: "none", "margin-left": "10px"})})
});

在beforeEach的时候,我们设定了固定的DOM进去,按照用户的行为做一些相应的操作。接着依据这个DOM中的元素变化 ,来作一些断言。

那么,即使我们已经有一个固定的DOM,想要监听这个DOM的变化就是一件容易的事。在我们断言之前,我们就会有一个新的DOM。我们只需要Diff一下这两个DOM的变化,就可以生成这部分测试代码。

virtual-dom与HyperScript

在寻觅中发现了virtual-dom这个库,一个可以支持创建元素、diff计算以及patch操作的库,并且它效率好像还不错。

virtual-dom可以说由下面几部分组成的:

  1. createElement,用于创建virtual Node。

  2. diff,顾名思义,diff算法。

  3. h,用于创建虚拟树的DSL——HyperScript。HyperScript是一个JavaScript的HyperText。

  4. patch,用于patch修改的内容。

举例来说,我们有下面一个生成Virtual DOM的函数:

function render(count)  {return h('div', {style: {textAlign: 'center',lineHeight: (100 + count) + 'px',border: '1px solid red',width: (100 + count) + 'px',height: (100 + count) + 'px'}}, [String(count)]);
}

render函数用于生成一个Virtual Node。在这里,我们可以将我们的变量传进去,如1。就会生成如下图所示的节点:

{"children": [{"text": "1"}],"count": 1,"descendantHooks": false,"hasThunks": false,"hasWidgets": false,"namespace": null,"properties": {"style": {"border": "1px solid red","height": "101px","lineHeight": "101px","textAlign": "center","width": "101px"}},"tagName": "DIV"
}

其中包含中相对应的属性等等。而我们只要调用createElement就可以创建出这个DOM。

如果我们修改了这个节点的一些元素,或者我们render了一个count=2的值时,我们就可以diff两个DOM。如:

virtualDom.diff(render(2), render(1))

根据两个值的变化就会生成如下的一个对象:

{"0": {"patch": {"style": {"height": "101px","lineHeight": "101px","width": "101px"}},"type": 4,"vNode": {...}},"1": {"patch": {"text": "1"},"type": 1,"vNode": {"text": "2"}},...
}

第一个对象,即0中包含了一些属性的变化。而第二个则是文本的变化——从2变成了1。我们所要做的测试生成便是标记这些变化,并记录之。

标记DOM变化

由于virtual-dom依赖于虚拟节点vNode,我们需要将fixtures转换为hyperscript。这里我们就需要一个名为html2hyperscript的插件,来解析html。接着,我们就可以diff转换完后的DOM:

var leftNode = "", rightNode = "";
var fixtures = '<div id="example"><h1 class="hello">Hello World</h1></div>';
var change = '<div id="example"><h1 class="hello">Hello World</h1><h2>fs</h2></div>';
parser(fixtures, function (err, hscript) {leftNode = eval(hscript);
});parser(change, function (err, hscript) {rightNode = eval(hscript);
});var patches = diff(leftNode, rightNode);

接着,我们需要调用patch函数来做一些相应的改变。

luffa.patch(virtualDom.create(leftNode), patches)

并且,我们可以尝试在patch阶段做一些处理——输出修改:

function printChange(originRootNodeHTML, applyNode) {var patchType;for (var patchIndex = 0; patchIndex < applyNode.newNodes.length; patchIndex++) {patchType = applyNode.newNodes[patchIndex].method;switch (patchType) {case 'insert':printInsert(applyNode);break;case 'node':printNode(applyNode, originRootNodeHTML, patchIndex);break;case 'remove':printRemove(applyNode, originRootNodeHTML, patchIndex);break;case 'string':printString(applyNode, originRootNodeHTML, patchIndex);break;case 'prop':printProp(applyNode, originRootNodeHTML, patchIndex);break;default:printDefault(applyNode, originRootNodeHTML, patchIndex);}}
}

根据不同的类型,作一些对应的输出处理,如pringNode:

function printNode(applyNode, originRootNodeHTML, patchIndex) {var originNode = $(applyNode.newNodes[patchIndex].vNode).prop('outerHTML') || $(applyNode.newNodes[patchIndex].vNode).text();var newNode = $(applyNode.newNodes[patchIndex].newNode).prop('outerHTML');console.log('%c' + originRootNodeHTML.replace(originNode, '%c' + originNode + '%c') + ', %c' + newNode, luffa.ORIGIN_STYLE, luffa.CHANGE_STYLE, luffa.ORIGIN_STYLE, luffa.NEW_STYLE);
}

用Chrome的console来标记修改的部分,及添加的部分。
图片描述
最后,我们似乎就可以生成相应的测试代码了。。。

其他

源码见:https://github.com/phodal/luffa
原文:基于Virtual DOM与Diff DOM的测试代码生成


http://lihuaxi.xjx100.cn/news/257931.html

相关文章

周志华,李航来智源大会了!

Datawhale会议 AI领域&#xff1a;2021北京智源大会Datawhale成员也会去智源大会&#xff0c;后台回复“北京”&#xff0c;可加入组织线下面基交流文末附报名参会方式长按识别二维码&#xff0c;即刻免费注册参会或点击「阅读原文」&#xff0c;直接注册报名&#xff01;

认识一下Kotlin语言,Android平台的Swift

今天在CSDN首页偶然看到一个贴子JetBrains正式公布Kotlin 1.0&#xff1a;JVM和Android上更好用的语言 看完后&#xff0c;感觉Kotlin语法非常简洁&#xff0c;有一系列动态语言的特点&#xff0c;Lambda表达式。高阶函数。闭包&#xff0c;甚至有静态语言的类型检查&#xff…

MIT用GPT-3冒充哲学家,竟骗过了一大半专家

视学算法报道 编辑&#xff1a;David Joey【导读】AI冒充哲学家&#xff0c;成功率有多少&#xff1f;一半的人分不出来。Daniel Dennett是一位哲学家&#xff0c;最近他有了一个「AI替身」。如果你问他&#xff0c;人是否可以造出一个有信仰、有欲望的机器人&#xff0c;他会…

看了 4K 经典中视频,我才知道 30 多年前的艺术家有多牛

作者 | 宋慧 出品 | AI科技大本营 提起童年记忆的国漫经典&#xff0c;你会想到哪一部&#xff1f;几十年前&#xff0c;水墨动画《小蝌蚪找妈妈》、手绘风《哪吒闹海》、木偶片《阿凡提》、剪纸艺术《葫芦兄弟》……这些老国产动画片&#xff0c;艺术风格千姿百态&#xff0c…

Zookeeper源码分析:Leader角色初始化

参考资料 <<从PAXOS到ZOOKEEPER分布式一致性原理与实践>> zookeeper-3.0.0Leader角色初始化 在上文的选举完成之后&#xff0c;每个zk实例都会根据选举结果进入对应的角色&#xff0c;本文主要就是讲述Leader的初始化相关内容。 Leader初始化流程 case LEADING…

什么是标记符控制的分水岭算法

本文首先指出传统分水岭算法的不足&#xff0c;然后介绍了标记符控制的分水岭算法&#xff0c;最后通过实例演示了该算法在图像分割中的应用。该微信图文由安晟提供。

[Flash开发笔记] 如何在as2.0中使用自定义类事件

as2编程中&#xff0c;我们通常要处理一些异步加载的数据&#xff0c;有点类似ajax中的callback&#xff0c;即我们不知道何时数据才会返回&#xff0c;并且只有当数据返回时&#xff0c;执行我们定义的操作。在flash6及以前&#xff0c;我们会常常碰到从外部加载一张图片或一段…

CentOS搭建msmtp+mutt实现邮件发送

1&#xff1a;搭建配置msmtp下载msmtp包&#xff1a;官方地址&#xff1a;http://msmtp.sourceforge.net/download.html编译&#xff0c;安装(官方下载的包为tar.xz格式):#xz -d msmtp-1.6.3.tar.xz #tar -xvf msmtp-1.6.3.tar #cd msmtp-1.6.3 #./configure --prefix /opt/app…