QUnit系列 -- 2.介绍单元测试(下)

  JavaScript测试框架:QUnit

  下面我们将介绍使用QUnit来完成前一章中的单元测试。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Refactored date examples</title>
 
    <link rel="stylesheet" href="../qunit.css" />
    <script src="../qunit.js"></script>
    <script src="prettydate.js"></script>
 
    <script>
    test("prettydate basics", function() {
        var now = "2008/01/28 22:25:00";
        equal(prettyDate(now, "2008/01/28 22:24:30"), "just now");
        equal(prettyDate(now, "2008/01/28 22:23:30"), "1 minute ago");
        equal(prettyDate(now, "2008/01/28 21:23:30"), "1 hour ago");
        equal(prettyDate(now, "2008/01/27 22:23:30"), "Yesterday");
        equal(prettyDate(now, "2008/01/26 22:23:30"), "2 days ago");
        equal(prettyDate(now, "2007/01/26 22:23:30"), undefined);
    });
    </script>
</head>
<body>
    <div id="qunit"></div>
</body>
</html>

  运行实例

 

  这里有三点需要注意。

  首先,我们包含了三个文件:两个和 QUnit 相关(qunit.css and qunit.js) ,另一个是包含测试函数的prettydate.js。

  其次,是断言部门的脚本。test方法只会调用一次,第一个参数是测试名称,第二个参数是实际的测试代码。然后代码定义了变量now,便于后面使用。接下来使用equal方法来执行断言操作,他是QUnit提供的一系列方法中的一个。第一个参数是函数执行后的结果,第二个参数是期望值,如果两个值一致则断言通过,否则就失败。

  最后,页面body中包含一些和QUnit相关的标签,这些元素是可选的。如果我们使用了,QUnit会使用他们来输出结果。

输出结果:

QUnit系列 -- 2.介绍单元测试(下)_第1张图片

包含失败的输出结果:

QUnit系列 -- 2.介绍单元测试(下)_第2张图片

 

  因为测试包含一个失败的断言,所以QUnit没有把结果收起来,我们可以马上看到错误。我们把期望值和实际值都显示了出来,还显示了他们的不同点,这样对于我们找到问题很有帮助。

 

  重构:2

  我们的断言还没结束,还没有判断“几个星期”的情况。 再次之前,我们再把代码重构下。现在的版本每次断言的时候,都会去调用 prettyDate,并传递 now 参数。 我们可以重构一个自定义的断言函数:

test("prettydate basics", function() {
    function date(then, expected) {
        equal(prettyDate("2008/01/28 22:25:00", then), expected);
    }
    date("2008/01/28 22:24:30", "just now");
    date("2008/01/28 22:23:30", "1 minute ago");
    date("2008/01/28 21:23:30", "1 hour ago");
    date("2008/01/27 22:23:30", "Yesterday");
    date("2008/01/26 22:23:30", "2 days ago");
    date("2007/01/26 22:23:30", undefined);
});

  运行实例

  这样我们就把prettyDate封装到了date函数中,date里面会提供now变量,这样外面使用的时候就不需要再传递了。这样会让我们的代码更简单。

  

  测试DOM操作

  现在prettyDate已经可以被很好的测试了,让我们回过头来看之前的例子。借助于window的load事件,我们可以使用prettyDate函数选择DOM元素并更新他们。和之前一样,我们需要重构他并使之能够测试。另外,我们把两个方法放到同一个模块中,避免命名空间的混乱并且让代码更容易维护。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Refactored date examples</title>
    <link rel="stylesheet" href="../qunit.css" />
    <script src="../qunit.js"></script>
    <script src="prettydate2.js"></script>
    <script>
    test("prettydate.format", function() {
        function date(then, expected) {
            equal(prettyDate.format("2008/01/28 22:25:00", then), expected);
        }
        date("2008/01/28 22:24:30", "just now");
        date("2008/01/28 22:23:30", "1 minute ago");
        date("2008/01/28 21:23:30", "1 hour ago");
        date("2008/01/27 22:23:30", "Yesterday");
        date("2008/01/26 22:23:30", "2 days ago");
        date("2007/01/26 22:23:30", undefined);
    });
 
    test("prettyDate.update", function() {
        var links = document.getElementById("qunit-fixture").getElementsByTagName("a");
        equal(links[0].innerHTML, "January 28th, 2008");
        equal(links[2].innerHTML, "January 27th, 2008");
        prettyDate.update("2008-01-28T22:25:00Z");
        equal(links[0].innerHTML, "2 hours ago");
        equal(links[2].innerHTML, "Yesterday");
    });
 
    test("prettyDate.update, one day later", function() {
        var links = document.getElementById("qunit-fixture").getElementsByTagName("a");
        equal(links[0].innerHTML, "January 28th, 2008");
        equal(links[2].innerHTML, "January 27th, 2008");
        prettyDate.update("2008/01/29 22:25:00");
        equal(links[0].innerHTML, "Yesterday");
        equal(links[2].innerHTML, "2 days ago");
    });
    </script>
</head>
<body>
    <div id="qunit"></div>
    <div id="qunit-fixture">
        <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">January 28th, 2008</a></span>
                    by <span class="author"><a href="/john/">John Resig</a></span>
                </small>
            </li>
            <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-27T22:24:17Z">January 27th, 2008</a></span>
                    by <span class="author"><a href="/john/">John Resig</a></span>
                </small>
            </li>
        </ul>
    </div>
</body>
</html>

  prettydate2.js代码:

var prettyDate = {
    format: function(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";
    },
 
    update: function(now) {
        var links = document.getElementsByTagName("a");
        for ( var i = 0; i < links.length; i++ ) {
            if ( links[i].title ) {
                var date = prettyDate.format(now, links[i].title);
                if ( date ) {
                    links[i].innerHTML = date;
                }
            }
        }
    }
};

  运行实例

 

  prettyDate.update方法是从之前例子中提取出来的,他含有now参数,方法里面把now传给了prettyDate.format。基于QUnit的测试,开始的时候会把所有包含在#qunit-fixture中的元素找出来。<div id="qunit-fixture">…</div>是页面新加入的标签,这里我们放置之前例子使用的DOM标签,这样我们就可以用于测试了。你不用担心这里DOM元素的改变会影响到其他的测试,因为QUnit会为每次测试重置这些标签的。

  这里你也许会有疑问,为什么测试页面没有显示<div id="qunit-fixture">…</div>的内容呢,原因是QUnit把他放在了一个离我们屏幕很远的地方,看截图:

QUnit系列 -- 2.介绍单元测试(下)_第3张图片

 

 

  重构:3

  上面的代码还是有很多重复的内容,我们进一步重构,把测试DOM的测试抽到方法domtest中,方便多次调用:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Refactored date examples</title>
    <link rel="stylesheet" href="../qunit.css" />
    <script src="../qunit.js"></script>
    <script src="prettydate2.js"></script>
    <script>
    test("prettydate.format", function() {
        function date(then, expected) {
            equal(prettyDate.format("2008/01/28 22:25:00", then), expected);
        }
        date("2008/01/28 22:24:30", "just now");
        date("2008/01/28 22:23:30", "1 minute ago");
        date("2008/01/28 21:23:30", "1 hour ago");
        date("2008/01/27 22:23:30", "Yesterday");
        date("2008/01/26 22:23:30", "2 days ago");
        date("2007/01/26 22:23:30", undefined);
    });
 
    function domtest(name, now, first, second) {
        test(name, function() {
            var links = document.getElementById("qunit-fixture").getElementsByTagName("a");
            equal(links[0].innerHTML, "January 28th, 2008");
            equal(links[2].innerHTML, "January 27th, 2008");
            prettyDate.update(now);
            equal(links[0].innerHTML, first);
            equal(links[2].innerHTML, second);
        });
    }
    domtest("prettyDate.update", "2008-01-28T22:25:00Z", "2 hours ago", "Yesterday");
    domtest("prettyDate.update, one day later", "2008/01/29 22:25:00", "Yesterday", "2 days ago");
    </script>
</head>
<body>
    <div id="qunit"></div>
    <div id="qunit-fixture">
        <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">January 28th, 2008</a></span>
                    by <span class="author"><a href="/john/">John Resig</a></span>
                </small>
            </li>
            <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-27T22:24:17Z">January 27th, 2008</a></span>
                    by <span class="author"><a href="/john/">John Resig</a></span>
                </small>
            </li>
        </ul>
    </div>
</body>
</html>

  运行实例

 

  回到开始

  重构之前,我们之前的代码会变的很简单:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Final date examples</title>
    <script src="prettydate2.js"></script>
    <script>
    window.onload = function() {
        prettyDate.update("2008-01-28T22:25:00Z");
    };
    </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>

  运行实例

 

  结论

  测试JavaScript不简单是使用测试运行器,写写测试用例。他要求你需要对代码结构作比较大的改变,以方便他能被执行单元测试。我们通过一个例子演示了如何实现特定的测试框架,也介绍了如何重构代码,以使他能使用QUnit做测试。QUnit提供了很多功能,例如支持测试异步代码,例如timeouts,ajax和事件。他的可视化测试运行器可以帮助我们debug代码,对特性测试的重新执行,并且方便我们跟踪错误信息。

 

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

你可能感兴趣的:(单元测试)