zoukankan      html  css  js  c++  java
  • QWrap的js单元测试工具

    QWrap的js/_tools下面,有几个工具,还是挺实用的。
    今天介绍一下单元测试工具。
    QWrap的单元测试(unittest)工具,是基于jsspec(http://jania.pe.kr/aw/moin.cgi/JSSpec)的语法来的。其css也是完全照搬jsspec。
    相对于jsspec,改变有:代码重构、弃用多次运行(仅对未通过的试例)策略、移去对原型的渲染、部分功能增删。

    如何使用qwrap的jsspec:
    <link rel="stylesheet" type="text/css" href="http://dev.qwrap.com/resource/js/_tools/unittest/JSSpec.css" />
    <script type="text/javascript" src="http://dev.qwrap.com/resource/js/_tools/unittest/UnitTest.js"></script>
    <script type="text/javascript" src="...自己的代码文件"></script>
    <script>/*单元测试代码*/</script>
    例如:
    View Code
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
    <html>
    <head>
    <title>UnitTest</title>
    <meta http-equiv="Content-Type" content="text/html; charset=GB2312" />
    <link rel="stylesheet" type="text/css" href="http://dev.qwrap.com/resource/js/_tools/unittest/JSSpec.css" />
    <script type="text/javascript" src="http://dev.qwrap.com/resource/js/_tools/unittest/UnitTest.js"></script>
    <script type="text/javascript" >
    //自己的代码
    //
    String相关方法:
    function trim(s){
    return s.replace(/^\s+|\s+$/g,'');
    }
    function camelize(s){
    return s.replace(/-(\w)/g, function(a,b){return b.toUpperCase();});
    }

    //Number相关
    function add50(n){
    return n+50;
    }
    function mul5(n){
    return n*5;
    };
    </script>

    <script type="text/javascript">// <![CDATA[
    //
    测试用例代码

    describe(
    'String相关', {
    'trim': function() {
    value_of(trim(
    "foo")).should_be('foo');
    value_of(trim(
    " foo")).should_be('foo');
    value_of(trim(
    " foo ")).should_be('foo');
    },
    'camelize': function() {
    value_of(camelize(
    "abc")).should_be('abc');
    value_of(camelize(
    "abc-def")).should_be('abcDef');
    value_of(camelize(
    "abc-def-hij")).should_be('abcDefHij');
    }
    });

    describe(
    'Number相关', {
    'add50': function() {
    value_of(add50(
    1)).should_be(51);
    value_of(add50(
    3)).should_be(53);
    value_of(add50(
    -5)).should_be(45);
    },
    'mul5': function() {
    value_of(mul5(
    0)).should_be(0);
    value_of(mul5(
    3)).should_be(15);
    value_of(mul5(
    -3)).should_be(-15);
    }
    });
    // ]]></script>
    </head>
    <body>
    </body>
    </html>

    点击运行以上代码。http://dev.qwrap.com/resource/js/_tools/unittest/_examples/UnitTest_4cnblogs.html
    那么我们具体看一下如何写单元测试代码。
    这是一段典型的代码:
    View Code
    describe('String相关', {
    'trim': function() {
    value_of(trim(
    "foo")).should_be('foo');
    value_of(trim(
    " foo")).should_be('foo');
    value_of(trim(
    " foo ")).should_be('foo');
    },
    'camelize': function() {
    value_of(camelize(
    "abc")).should_be('abc');
    value_of(camelize(
    "abc-def")).should_be('abcDef');
    value_of(camelize(
    "abc-def-hij")).should_be('abcDefHij');
    }
    });

    UnitTest.js会在window下产生三个变量:
        UnitTest命名空间。
        describe函数。用来定义一个“测试用例组”。格式为:“测试用例名”与“测试用例函数”的键值对json对象。
        value_of函数。用来产生一个主语。
    页面会在onload后按顺序执行测试用例,并在页面产生测试报告。报告是即时演染的。
    测试用例有四种状态:未运行/运行中/未通过/已通过,分别以白色/黄色/绿色/红色象征。
    每个测试用例函数里可以有多个断言。如果所有的断言都正确的话,则这个测试用例通过检查。


    断言的形式通常为:
    //var a=3-2;
    value_of(a).should_be(1);//主(o).谓(宾);
    详解:
    value_of(a)//以a为中心,创建一个主语。
    .should_be//谓语
    (1);//1是宾语。

    “主(o).谓(宾);”断言中,常用的谓语有:
    should_be(value)、should_not_be(value)、should_have_method(methodName)、should_have_property(propertyName);
    扩展的写法还有: should(op,value);
    其中,op为比较符。如:
    value_of(a).should('===',1);
    value_of(a).should('!==',3);
    value_of(a).should('<',3);
    value_of(a).should('>',0);

    还有一些其它用法,这里就不一一详举了。
    很多谓语都是由这个万能谓语演化出来的:
    _should: function(property,value,op,selfPre,selfTail,valuePre,valueTail,isReverse)。
    目前unittest只提供以下谓语,如果觉得不够用的话,可以自己添加。
    实现参考unittest.js中的这部分代码:
    View Code
    mix(Subject.prototype,{
    _should:
    function(property,value,op,selfPre,selfTail,valuePre,valueTail,isReverse){
    var selfDesc=[
    selfPre,
    property
    ==null?"self":"self[property]",
    selfTail
    ],
    valueDesc
    =[
    valuePre,
    "value",
    valueTail
    ];
    var desc=[].concat( isReverse?valueDesc:selfDesc, op, isReverse?selfDesc:valueDesc );
    var sFun=desc.join(" ");
    //alert([sFun,this.self,property,value]);
    var tempCur={
    self:
    this.self,
    property:property,
    value:value,
    sFun:sFun
    };
    try{
    var fun=new Function("self","property","value","return ("+sFun+");");
    }
    catch(ex){//错误的调用了_should方法,造成matcher不合法
    currentCaseStatus|=2;
    currentErrorMsg
    ="Matcher is illegle: "+ex.message;
    currentShouldInfo
    =tempCur;
    return;
    }
    try{
    var result=fun(this.self,property,value);
    }
    catch(ex){//运行matcher时抛错
    currentCaseStatus|=4;
    currentErrorMsg
    ="Not match: "+ex.message;
    currentShouldInfo
    =tempCur;
    return;
    }
    if(result !== true) {//Not Match
    currentCaseStatus|=4;
    currentErrorMsg
    ="Not match";
    currentShouldInfo
    =tempCur;
    return;
    }
    currentCaseStatus
    |=1;//Match
    return result;
    },
    should:
    function (op,value){
    return this._should(null,value,op);
    },
    should_be:
    function (value){
    return this._should(null,value,"===");
    },
    should_not_be:
    function (value){
    return this._should(null,value,"!==");
    },
    should_have_method:
    function(methodName){
    return this._should(methodName,"function","==","typeof");
    },
    should_have_property:
    function(property){
    return this._should(null,property,"in",null,null,null,null,true);
    },
    should_contains:
    function(value){
    return this._should(null,value,".contains",null,null,"(",")");
    },
    property_should:
    function (property,op,value){
    return this._should(property,value,op);
    },
    property_should_be:
    function (property,value){
    return this._should(property,value,"===");
    },
    property_should_not_be:
    function (property,value){
    return this._should(property,value,"!==");
    },

    log:
    function(message){
    Logger.log(
    this.self,message);
    }
    });


    一个小问题:
    由于qwrap的单元测试每个用户只跑一次,导致有时候在某些浏览器下,错误行号不是我们想要的。
    所以在实际的应用中,可能会看到有的断言后面多了一个.line的尾巴:
    value_of(a).should('<',3).line
    它是解决行号不准的一个方案。不过某些编码规范检查工具会拒掉这种写法。可以改成“.toString()”来通过检查。

    另:其实这里的主语Subject:
    UnitTest.Subject=function (self){
        this.self=self;
    };
    window.value_of=function (self){
        return new UnitTest.Subject(self);
    };

    它也是一个QWrap里所说到的wrap(为了保护核对象(core)的纯净,在外面包一层皮(wrap))。不过,它的核的名字,不是叫qwrap所推荐的"core",而是叫"self"。


    单元测试在QWrap的开发中,已经有很成熟的应用,如:
    qwrap的core的单元测试:http://dev.qwrap.com/resource/js/core/_tests/UnitTest_Core.html
    qwrap的core_dom_retouch单元测试:http://dev.qwrap.com/resource/js/dom/_tests/UnitTest_Dom.html
    我们在修改代码后,都会运行一下单元测试,以确保以前的功能没有被改坏。


  • 相关阅读:
    如何写README.md
    (2020-03-29)--------paper list
    ROS(八)----示例
    ROS(七)----动态参数
    ROS(六)----参数
    ROS(四)---自定义消息.msg
    ROS(三)-----节点的定义
    ROS(二)-------RoboWare Studio
    ROS(一)-----ros 安装
    pytorch(4)----nn.Module、nn.functional、nn.Sequential、nn.optim
  • 原文地址:https://www.cnblogs.com/jkisjk/p/qwrap_unittest.html
Copyright © 2011-2022 走看看