QUnit系列 -- 1.介绍单元测试(上)

简介:   大家都知道单元测试对于保证代码质量的重要性,但是对客户端代码进行单元测试则要困难的多。一个比较棘手的问题是,因为JavaScript代码和后台代码或者html结合的比较紧密,他缺少真正单元的概念。例如对dom的操作,无论我们是借助jquery这样的类库,把js代码单独放在一个文件,还是直接使用内嵌代码的实现方式,都没有可以测试的单元。

  大家都知道单元测试对于保证代码质量的重要性,但是对客户端代码进行单元测试则要困难的多。一个比较棘手的问题是,因为JavaScript代码和后台代码或者html结合的比较紧密,他缺少真正单元的概念。例如对dom的操作,无论我们是借助jquery这样的类库,把js代码单独放在一个文件,还是直接使用内嵌代码的实现方式,都没有可以测试的单元。

  那么什么是单元呢。一般而言,单元就是一个功能函数,相同的输入,输出结果是一定的。这种情况的函数,做单元测试是相当简单的,但有时候你需要处理一些特殊情况,例如对dom的操作。对于我们来说他仍然是有用的,我们可以指出哪些代码可以构造到单元里面,然后对他做相应的测试。

 

  创建单元测试

  有了上面的指导思想,对于我们开始一项全新工作,并引入单元测试时相当简单的工作。不过本文介绍的内容是,帮助你对已有代码完善单元测试,我们需要解决下面的难题:提取现有代码,对重要部分作测试;发现潜在问题并加以修复。

  提取现有代码把他放到不同地方,而不影响现有功能,我们把这一过程称为重构,重构是改善代码设计相当有用的方式。任何对代码的修改都有可能影响现有功能,这也就体现了单元测试的重要性,他会让你的工作更有保障。而这时候我们还没有单元测试,所以需要借助手工测试的方式来保证任何代码的修改没有产生新的bug。我们现在有了理论基础,接下来要做的就是找个例子来实践下。下面的代码会找到所有包含 title 属性的连接, 然后根据情况显示过去了多少时间,例如: “5 days ago”:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Mangled date examples</title>
    <script>
    function prettyDate(time){
        var date = new Date(time || ""),
            diff = (((new Date()).getTime() - date.getTime()) / 1000),
            day_diff = Math.floor(diff / 86400);
 
        if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
            return;
 
        return day_diff == 0 && (
                diff < 60 && "just now" ||
                diff < 120 && "1 minute ago" ||
                diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
                diff < 7200 && "1 hour ago" ||
                diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
            day_diff == 1 && "Yesterday" ||
            day_diff < 7 && day_diff + " days ago" ||
            day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago";
    }
    window.onload = function() {
        var links = document.getElementsByTagName("a");
        for ( var i = 0; i < links.length; i++ ) {
            if ( links[i].title ) {
                var date = prettyDate(links[i].title);
                if ( date ) {
                    links[i].innerHTML = date;
                }
            }
        }
    };
    </script>
</head>
<body>
 
<ul>
    <li class="entry" id="post57">
        <p>blah blah blah...</p>
        <small class="extra">
            Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-28T20:24:17Z"><span>January 28th, 2008</span></a></span>
            by <span class="author"><a href="/john/">John Resig</a></span>
        </small>
    </li>
    <!-- 更多内容... -->
</ul>
 
</body>
</html>

  如果你运行代码,你会发现不是所有的时间会被替换。代码会查询页面中所有包含title属性的连接,然后对title执行prettyDate函数,如果函数返回结果则更新链接的innerHTML属性。

 

  让代码变得可测试

  问题在于对于大于31天的时间,prettyDate只是返回undefined,链接的内容不会发生变化。如果要看假定发生了什么,我需要硬编码一个当前时间。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Mangled date examples</title>
    <script>
    function prettyDate(now, time){
        var date = new Date(time || ""),
            diff = (((new Date(now)).getTime() - date.getTime()) / 1000),
            day_diff = Math.floor(diff / 86400);
 
        if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
            return;
 
        return day_diff == 0 && (
                diff < 60 && "just now" ||
                diff < 120 && "1 minute ago" ||
                diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
                diff < 7200 && "1 hour ago" ||
                diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
            day_diff == 1 && "Yesterday" ||
            day_diff < 7 && day_diff + " days ago" ||
            day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago";
    }
    window.onload = function() {
        var links = document.getElementsByTagName("a");
        for ( var i = 0; i < links.length; i++ ) {
            if ( links[i].title ) {
                var date = prettyDate("2008-01-28T22:25:00Z", links[i].title);
                if ( date ) {
                    links[i].innerHTML = date;
                }
            }
        }
    };
    </script>
</head>
<body>
 
<ul>
    <li class="entry" id="post57">
        <p>blah blah blah...</p>
        <small class="extra">
            Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-28T20:24:17Z"><span>January 28th, 2008</span></a></span>
            by <span class="author"><a href="/john/">John Resig</a></span>
        </small>
    </li>
    <-- 更多内容... -->
</ul>
 
</body>
</html>

  运行实例

  现在链接应该显示“2 hours ago”, “Yesterday”等,但是现在的代码仍然不是实际可测试的单元。在没有进一步改造代码的前提下,我们能测试的只是DOM的改变结果。虽然这样可以工作,但是对html代码很小的修改都有可能打破测试,对于这种测试的效费比很差。

 

  重构:1

  我们需要对代码重构,以使他变得可以单元测试。我们需要做两点改变:1.以参数的形式向prettyDate函数传递当前时间,这样程序里面就不需要使用new Date了;2.把函数抽到一个单独的文件中,这样他就变得可重用了。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Refactored date examples</title>
    <script src="prettydate.js"></script>
    <script>
    window.onload = function() {
        var links = document.getElementsByTagName("a");
        for ( var i = 0; i < links.length; i++ ) {
            if ( links[i].title ) {
                var date = prettyDate("2008-01-28T22:25:00Z", links[i].title);
                if ( date ) {
                    links[i].innerHTML = date;
                }
            }
        }
    };
    </script>
</head>
<body>
 
<ul>
    <li class="entry" id="post57">
        <p>blah blah blah...</p>
        <small class="extra">
            Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-28T20:24:17Z"><span>January 28th, 2008</span></a></span>
            by <span class="author"><a href="/john/">John Resig</a></span>
        </small>
    </li>
    <-- 更多内容... -->
</ul>
 
</body>
</html>

  prettydate.js代码:

function prettyDate(now, time){
    var date = new Date(time || ""),
        diff = (((new Date(now)).getTime() - date.getTime()) / 1000),
        day_diff = Math.floor(diff / 86400);
 
    if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 )
        return;
 
    return day_diff == 0 && (
            diff < 60 && "just now" ||
            diff < 120 && "1 minute ago" ||
            diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
            diff < 7200 && "1 hour ago" ||
            diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
        day_diff == 1 && "Yesterday" ||
        day_diff < 7 && day_diff + " days ago" ||
        day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago";
}

  运行实例

 

  现在我们就有些东西可以测试了,我们来做单元测试:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Refactored date examples</title>
    <script src="prettydate.js"></script>
    <script>
    function test(then, expected) {
        results.total++;
        var result = prettyDate("2008/01/28 22:25:00", then);
        if (result !== expected) {
            results.bad++;
            console.log("Expected " + expected + ", but was " + result);
        }
    }
    var results = {
        total: 0,
        bad: 0
    };
    test("2008/01/28 22:24:30", "just now");
    test("2008/01/28 22:23:30", "1 minute ago");
    test("2008/01/28 21:23:30", "1 hour ago");
    test("2008/01/27 22:23:30", "Yesterday");
    test("2008/01/26 22:23:30", "2 days ago");
    test("2007/01/26 22:23:30", undefined);
    console.log("Of " + results.total + " tests, " + results.bad + " failed, "
        + (results.total - results.bad) + " passed.");
    </script>
</head>
<body>
 
</body>
</html>

  运行实例(确保允许使用控制台,例如firebug或者chrome的web inspector)

  

  这样我们就创建了一个特定的测试架构,使用控制台输出结果。他不依赖DOM,所以我们可以在一个没有浏览器的JavaScript环境中运行它,例如Node.js或者Rhino。测试失败,会显示出期望值和实际值。最后会显示出失败和成功总数。如果全部成功,结果应该类似于这样:

Of 6 tests, 0 failed, 6 passed.

失败的情况:

Expected 2 day ago, but was 2 days ago.

Of 6 tests, 1 failed, 5 passed.

  

  虽然我们可以使用特定的解决方案完成测试,但是借助已有的测试框架可以帮助我们更好的完成工作,他为我们提供了更好的结果展示,更多的命令等等。下节我们将介绍QUnit的使用。

 

文章来源:http://qunitjs.com/intro/

目录
相关文章
|
JavaScript 前端开发 测试技术
|
JavaScript 前端开发 机器人
|
Web App开发 JavaScript 前端开发
QUnit系列 -- 2.介绍单元测试(下)
  JavaScript测试框架:QUnit   下面我们将介绍使用QUnit来完成前一章中的单元测试。 DOCTYPE html> Refactored date examples test...
901 0
|
6天前
|
JSON Java 测试技术
SpringCloud2023实战之接口服务测试工具SpringBootTest
SpringBootTest同时集成了JUnit Jupiter、AssertJ、Hamcrest测试辅助库,使得更容易编写但愿测试代码。
34 3
|
1月前
|
JSON 算法 数据可视化
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
这篇文章是关于如何通过算法接口返回的目标检测结果来计算性能指标的笔记。它涵盖了任务描述、指标分析(包括TP、FP、FN、TN、精准率和召回率),接口处理,数据集处理,以及如何使用实用工具进行文件操作和数据可视化。文章还提供了一些Python代码示例,用于处理图像文件、转换数据格式以及计算目标检测的性能指标。
57 0
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
|
2月前
|
移动开发 JSON Java
Jmeter实现WebSocket协议的接口测试方法
WebSocket协议是HTML5的一种新协议,实现了浏览器与服务器之间的全双工通信。通过简单的握手动作,双方可直接传输数据。其优势包括极小的头部开销和服务器推送功能。使用JMeter进行WebSocket接口和性能测试时,需安装特定插件并配置相关参数,如服务器地址、端口号等,还可通过CSV文件实现参数化,以满足不同测试需求。
237 7
Jmeter实现WebSocket协议的接口测试方法
|
2月前
|
JSON 移动开发 监控
快速上手|HTTP 接口功能自动化测试
HTTP接口功能测试对于确保Web应用和H5应用的数据正确性至关重要。这类测试主要针对后台HTTP接口,通过构造不同参数输入值并获取JSON格式的输出结果来进行验证。HTTP协议基于TCP连接,包括请求与响应模式。请求由请求行、消息报头和请求正文组成,响应则包含状态行、消息报头及响应正文。常用的请求方法有GET、POST等,而响应状态码如2xx代表成功。测试过程使用Python语言和pycurl模块调用接口,并通过断言机制比对实际与预期结果,确保功能正确性。
247 3
快速上手|HTTP 接口功能自动化测试
|
1月前
|
JavaScript 前端开发 API
vue尚品汇商城项目-day02【9.Home组件拆分+10.postman测试接口】
vue尚品汇商城项目-day02【9.Home组件拆分+10.postman测试接口】
40 0
|
2月前
|
JavaScript 前端开发 测试技术
ChatGPT与接口测试
ChatGPT与接口测试,测试通过
48 5