资讯专栏INFORMATION COLUMN

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

CoXie / 3354人阅读

摘要:但是,我还是觉得这是一个非常不错的话题测试代码生成。,用于创建虚拟树的。,用于修改的内容。而第二个则是文本的变化从变成了。我们所要做的测试生成便是标记这些变化,并记录之。其他源码见原文基于与的测试代码生成

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

当我们在写一些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可以说由下面几部分组成的:

createElement,用于创建virtual Node。

diff,顾名思义,diff算法。

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

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 = "

Hello World

"; var change = "

Hello World

fs

"; 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的测试代码生成

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78347.html

相关文章

  • 你不知道Virtual DOM(五):自定义组件

    摘要:现在流行的前端框架都支持自定义组件,组件化开发已经成为提高前端开发效率的银弹。二对自定义组件的支持要想正确的渲染组件,第一步就是要告诉某个标签是自定义组件。下面的例子里,就是一个自定义组件。解决了识别自定义标签的问题,下一步就是定义标签了。 欢迎关注我的公众号睿Talk,获取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、...

    lk20150415 评论0 收藏0
  • 解读React源码(一):初探React源码

    摘要:前言的基本概念组件的构建方法以及高级用法这背后的一切如何运转深入内部的实现机制和原理初探源码代码组织结构包含一系列的工具方法插件包含一系列同构方法包含一些公用或常用方法如等包含一些测试方法等包含一些边界错误的测试用例是代码的核心部分它包含了 前言 React的基本概念,API,组件的构建方法以及高级用法,这背后的一切如何运转,深入Virtual DOM内部的实现机制和原理. 初探Rea...

    Eminjannn 评论0 收藏0
  • 你不知道Virtual DOM(三):Virtual Dom更新优化

    摘要:经过这次优化,计算的时间快了那么几毫秒。基于当前这个版本的代码还能做怎样的优化呢,请看下一篇的内容你不知道的四的作用。 欢迎关注我的公众号睿Talk,获取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 目前最流行的两大前端框架,React和Vue,都不约而同的借助Virtual DOM技术提高页面的渲染效率。那么,什...

    xiongzenghui 评论0 收藏0
  • 去哪儿网迷你React研发心得

    摘要:市面上竟然拥有多个虚拟库。虚拟库,就是出来后的一种新式库,以虚拟与算法为核心,屏蔽操作,操作数据即操作视图。及其他虚拟库已经将虚拟的生成交由与处理了,因此不同点是,虚拟的结构与算法。因此虚拟库是分为两大派系算法派与拟态派。 去哪儿网迷你React是年初立项的新作品,在这前,去哪儿网已经深耕多年,拥有QRN(react-native的公司制定版),HY(基于React的hybird方案)...

    pekonchan 评论0 收藏0
  • React源码分析实现(三):实操DOM Diff

    摘要:速度略有损失,但可读性大大提高。与传统对比传统的算法通过循环递归每一个节点,进行对比,这样的操作效率非常的低,复杂程度其中标识树的节点总数。 原文链接:Nealyang PersonalBlog 由于源码中diff算法掺杂了太多别的功能模块,并且dom diff相对于之前的代码实现来说还是有些麻烦的,尤其是列表对比的算法,所以这里我们单独拿出来说他实现 前言 众所周知,React中最...

    Drummor 评论0 收藏0

发表评论

0条评论

CoXie

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<