Javascript學習-簡單測試環境

Javascript學習-簡單測試環境

在《JavaScript忍者祕籍》2.4測試條件基礎知識中,做者給出了一個精簡版的assert和assert組的實現,對於初學者而言,這無疑是一個很好的例子,既讓咱們獲得了一個好用的小工具,又讓咱們看到了用javascript實現這個工具是如此的簡單。javascript

這裏主要是從代碼角度最2.4章節作一些補充和說明,包括原有代碼中的一些bug及其修正。固然了,既然涉及到了代碼解析,這就不能說是初學者的範疇了,至少要多javascript中的函數聲明,函數實現,函數閉包等內容有了基本的瞭解後,才能看懂這篇文章。css

1.assert

先來講說assert,應用代碼是這個樣子的:html

        <script type="text/javascript">
            assert(1 + 1 === 2, "1 + 1 = 2");
            assert(1 + 1 === 3, "1 + 1 = 3");
        </script>

assert就是一個javascript函數,有兩個參數,第一個參數用來判斷表達式是true或false,第二個參數用來對測試作一些說明,測試結果直接顯示在html中,這裏的測試結果是這個樣子的:java

還挺酷的吧。好了,那麼咱們就來看看如何實現這個assert?閉包

首先新建一個html文件app

而後在body中加入一個id爲rusults的ul節點:ide

    <body>
    	<ul id="results"></ul>
    </body>

 後面全部的assert結果列表都要加到這個節點下。函數

assert執行完成後的html結果是這樣的:工具

看起來挺簡單的吧,就是在ul節點下對每一個assert測試用li節點來表現。對於測試爲true的li節點的class被賦值爲pass,對於測試爲false的li節點的class被賦值爲fail。學習

原理清楚了,那麼這個assert函數的代碼看起來就不復雜了:

            function assert(value, desc) {
                // 建立li節點
                var li = document.createElement("li");
                // 若是value爲true,li的class爲pass
                // 若是value爲false,li的class爲fail
                li.className = value ? "pass" : "fail";
                // 根據desc建立text節點,而後添加到li節點下
                li.appendChild(document.createTextNode(desc));
                // 找到document中id爲results的節點元素,就是那個body下的ul,
                // 而後把新建的li節點添加到ul節點下
                document.getElementById("results").appendChild(li);
            }

剩下的就是添加一些css,美化一下html了,既然已經學習javascript了,通常的html和css的內容就不在這說了,完整的html以下:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>assert</title>
        <style>
            body {
                font-family: sans-serif;
                font-size: 12pt;
            }

            #results {
                background-color: #e0e0e0;
                border-radius: 1em;
                padding: 1em;
                list-style-position: inside;
            }

            ul {
                list-style-type : circle;
            }

            #results li {
                margin-bottom: 0.2em;
            }

            #results li.fail {
                color: red;
                text-decoration: line-through;
            }

            #results li.pass {
                color: green;
            }
        </style>
        <script type="text/javascript">
            function assert(value, desc) {
                // 建立li節點
                var li = document.createElement("li");
                // 若是value爲true,li的class爲pass
                // 若是value爲false,li的class爲fail
                li.className = value ? "pass" : "fail";
                // 根據desc建立text節點,而後添加到li節點下
                li.appendChild(document.createTextNode(desc));
                // 找到document中id爲results的節點元素,就是那個body下的ul,
                // 而後把新建的li節點添加到ul節點下
                document.getElementById("results").appendChild(li);
            }
        </script>
    </head>
    <body>
        <ul id="results"></ul>

        <script type="text/javascript">
            assert(1 + 1 === 2, "1 + 1 = 2");
            assert(1 + 1 === 3, "1 + 1 = 3");
        </script>
    </body>
</html>

2.asserts

asserts表明一個測試組,應用代碼是這個樣子的:

        <script type="text/javascript">
            asserts();

            test("數值計算測試", function() {
                assert(1 + 1 === 2, "1 + 1 = 2");
            });

            test("字符串測試", function() {
                assert("a" + "b" === "ab", '"a" + "b" = "ab"');
                assert("a" + "b" === "abc", '"a" + "b" = "abc"');
            });
        </script>

這段代碼是說,先建立一個描述爲「數值計算測試」的測試組,裏面加一組assert;再建立一個描述爲「字符串測試」的測試組,裏面加一組assert。兩個測試組之間是平級的關係。

每一個測試組裏的一組assert都是不一樣的,所以須要一個function包裝起來。這個函數能夠叫作「測試組assert組裝函數」。

這裏的測試結果是這個樣子的:

看起來更酷了吧。你注意到了沒有,這裏有一個需求點:若是測試組裏面有一個assert測試爲false,那麼整個測試組也要標記爲false。

這個html的結構以下:

每一個測試組用li節點表現,而li節點下又內嵌了一個ul節點,在這個內嵌的ul節點下才是測試組內全部assert的li節點表現。

固然了,有圖有真相,這裏很明顯有一個小bug,"a" + "b" === "ab"明明是正確的,怎麼顯示的li節點也被畫紅線了?或許你也能夠辯解爲既然是整個測試組失敗了,那麼爲這個正確的li節點畫紅線也說得過去吧?誰讓它屬於失敗的測試組呢?既然選擇了豬同樣的隊友,那就得認命。但是那你又怎麼解釋這個正確的li節點被一邊畫了紅線,一邊卻顯示爲綠色的文字?這明顯自相矛盾嘛。

好了,先不理這個小bug了,稍後咱們會解決這個bug。如今仍是讓咱們老老實實的來看看這個測試組的功能是如何實現的吧?

html相關的部分都不改,body裏仍是那個孤零零的id爲rusults的ul節點,css也徹底不用動,須要修改的只有javascript代碼。

注意測試組的代碼中先調用了一個asserts函數,這個函數負責初始化測試組的一些環境,簡單的說它是這個樣子的:

            // 測試組的總體初始化
            function asserts() {
                // 聲明一個results變量,
                // 做爲assert函數閉包和test函數閉包的一部分
                var results;

                // 建立assert表現節點
                assert = function(value, desc) {
                }

                // 建立測試組表現節點
                test = function(name, fn) {
                }
            }

這裏這裏對assert從新進行了賦值,固然咱們首先須要知道這種assert以前沒有var聲明的,說明這個變量已經在全局的window中,或者將在這句代碼執行處被加入到了全局的window中,而咱們上面在說單個assert的時候不是已經有了一個assert函數的實現了嗎?那個assert也是在全局的window中的。毫無疑問,在調用asserts函數後,原有的assert函數就被覆蓋掉了。test變量也是相似的,在調用asserts函數後,將被加入到全局的window中。

注意asserts函數開始的那個results變量,由於asserts函數調用後會在全局的window增長兩個函數assert和test,而這個results變量就必然的成爲了這兩個函數閉包的一部分。

咱們先看看這個test函數是如何實現的:

                test = function(name, fn) {
                    // 找到document中id爲results的ul節點元素,就是那個body下的ul
                    results = document.getElementById("results");
                    // 建立一個ul節點
                    var ul = document.createElement("ul");
                    // 建立一個測試組節點,就象建立普通assert節點同樣直接調用assert
                    // 毫無心外,這個測試組節點被加到了id爲results的ul節點元素下,
                    // 初始默認這個測試組節點的測試結果是true。
                    // 在測試組節點下添加內嵌的ul節點,該測試組下的全部assert表現節點都會被
                    // 加入到這個內嵌的ul節點中。
                    // 既然如此,那麼咱們就讓results變量指向這個內嵌的ul節點
                    results = assert(true, name).appendChild(ul);

                    // 調用"測試組assert組裝函數",構建各個assert表現節點
                    fn();
                };

在test函數執行的開始,results被指向了body下的ul節點,並在此基礎上完成了測試組表現節點的建立,而後results被指向了測試組內嵌的ul節點上,"測試組assert組裝函數"被調用,新的assert表現節點就會被加入到results節點下。

下面咱們來看看新的assert函數是如何實現的:

                assert = function(value, desc) {
                    // 建立li節點
                    var li = document.createElement("li");
                    // 若是value爲true,li的class爲pass
                    // 若是value爲false,li的class爲fail
                    li.className = value ? "pass" : "fail";
                    // 根據desc建立text節點,而後添加到li節點下
                    li.appendChild(document.createTextNode(desc));
                    // 把新建的li節點添加到results下,至於這個rusults是啥?
                    // 在test執行前是body下的ul節點
                    // 在test執行後是測試組表現節點下的ul節點
                    results.appendChild(li);

                    if (!value) {
                        // 若是有一個assert測試結果是false,
                        // 那麼就找到li節點的父節點的父節點,
                        // 也就是測試組表現節點了,而後設置class爲fail
                        li.parentNode.parentNode.className = "fail";
                    }

                    // 返回li節點
                    // 在test執行前是測試組表現節點
                    // 在test執行後是assert表現節點
                    return li;
                };

好了,搞定,完整的html以下:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>assert</title>
        <style>
            body {
                font-family: sans-serif;
                font-size: 12pt;
            }

            #results {
                background-color: #e0e0e0;
                border-radius: 1em;
                padding: 1em;
                list-style-position: inside;
            }

            ul {
                list-style-type : circle;
            }

            #results li {
                margin-bottom: 0.2em;
            }

            #results li.fail {
                color: red;
                text-decoration: line-through;
            }

            #results li.pass {
                color: green;
            }
        </style>
        <script type="text/javascript">
            // 測試組的總體初始化
            function asserts() {
                // 聲明一個results變量,
                // 做爲assert函數閉包和test函數閉包的一部分
                var results;

                assert = function(value, desc) {
                    // 建立li節點
                    var li = document.createElement("li");
                    // 若是value爲true,li的class爲pass
                    // 若是value爲false,li的class爲fail
                    li.className = value ? "pass" : "fail";
                    // 根據desc建立text節點,而後添加到li節點下
                    li.appendChild(document.createTextNode(desc));
                    // 把新建的li節點添加到results下,至於這個rusults是啥?
                    // 在test執行前是body下的ul節點
                    // 在test執行後是測試組表現節點下的ul節點
                    results.appendChild(li);

                    if (!value) {
                        // 若是有一個assert測試結果是false,
                        // 那麼就找到li節點的父節點的父節點,
                        // 也就是測試組表現節點了,而後設置class爲fail
                        li.parentNode.parentNode.className = "fail";
                    }

                    // 返回li節點
                    // 在test執行前是測試組表現節點
                    // 在test執行後是assert表現節點
                    return li;
                };

                test = function(name, fn) {
                    // 找到document中id爲results的ul節點元素,就是那個body下的ul
                    results = document.getElementById("results");
                    // 建立一個ul節點
                    var ul = document.createElement("ul");
                    // 建立一個測試組節點,就象建立普通assert節點同樣直接調用assert
                    // 毫無心外,這個測試組節點被加到了id爲results的ul節點元素下,
                    // 初始默認這個測試組節點的測試結果是true。
                    // 在測試組節點下添加內嵌的ul節點,該測試組下的全部assert表現節點都會被
                    // 加入到這個內嵌的ul節點中。
                    // 既然如此,那麼咱們就讓results變量指向這個內嵌的ul節點
                    results = assert(true, name).appendChild(ul);

                    // 調用"測試組assert組裝函數",構建各個assert表現節點
                    fn();
                };
            }
        </script>
    </head>
    <body>
        <ul id="results"></ul>

        <script type="text/javascript">
            asserts();

            test("數值計算測試", function() {
                assert(1 + 1 === 2, "1 + 1 = 2");
            });

            test("字符串測試", function() {
                assert("a" + "b" === "ab", '"a" + "b" = "ab"');
                assert("a" + "b" === "abc", '"a" + "b" = "abc"');
            });
        </script>
    </body>
</html>
View Code

3.修正測試組爲false的bug

之因此有這個bug,是由於這裏的測試組表現太簡單了,直接在li節點上設置class,使得css的可控性不高。學過css列表部分的應該都清楚,對列表的控制應該使用行內文本span嘛。

咱們但願的顯示效果應該是:

相應的html結構應該是:

 既然只是將測試組表現節點和測試表現節點多加一層span,那麼test函數是徹底不用變的,只是assert函數須要作一點小的修改:

                assert = function(value, desc) {
                    // 建立li節點
                    var li = document.createElement("li");
                    // 建立sapn節點
                    var span = document.createElement("span");
                    // 根據desc建立text節點
                    var text = document.createTextNode(desc);

                    // 在li下添加span節點
                    li.appendChild(span);
                    // 在span下添加text節點
                    // 完成li>span>text的三層關係
                    span.append(text);

                    // 若是value爲true,span的class爲pass
                    // 若是value爲false,span的class爲fail
                    span.className = value ? "pass" : "fail";

                    // 把新建的li節點添加到results下,至於這個rusults是啥?
                    // 在test執行前是body下的ul節點
                    // 在test執行後是測試組表現節點下的ul節點
                    results.appendChild(li);
                    
                    if (!value) {
                        // 若是有一個assert測試結果是false,
                        // 那麼就找到li節點的父節點的父節點,
                        // 也就是測試組表現節點了
                        var liGroup = li.parentNode.parentNode;
                        // 不能直接在測試組表現節點設置class了
                        // 必須在測試組表現節點下的span節點設置class
                        // 也就是測試組表現節點下的第一個子元素
                        liGroup.childNodes[0].className = "fail";
                    }
                    
                    // 返回li節點
                    // 在test執行前是測試組表現節點
                    // 在test執行後是assert表現節點
                    return li;
                };

相應的css也是須要作些小的修改的,不是直接在li節點上作效果了,而是在span節點上作效果。這些小地方都很容易理解,那麼就直接上修改後的完整html吧:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>assert</title>
        <style>
            body {
                font-family: sans-serif;
                font-size: 12pt;
            }

            #results {
                background-color: #e0e0e0;
                border-radius: 1em;
                padding: 1em;
                list-style-position: inside;
            }

            ul {
                list-style-type : circle;
            }

            #results li {
                margin-bottom: 0.2em;
            }

            #results span.fail {
                color: red;
                text-decoration: line-through;
            }

            #results span.pass {
                color: green;
            }
        </style>
        <script type="text/javascript">
            // 測試組的總體初始化
            function asserts() {
                // 聲明一個results變量,
                // 做爲assert函數閉包和test函數閉包的一部分
                var results;
                
                assert = function(value, desc) {
                    // 建立li節點
                    var li = document.createElement("li");
                    // 建立sapn節點
                    var span = document.createElement("span");
                    // 根據desc建立text節點
                    var text = document.createTextNode(desc);

                    // 在li下添加span節點
                    li.appendChild(span);
                    // 在span下添加text節點
                    // 完成li>span>text的三層關係
                    span.append(text);

                    // 若是value爲true,span的class爲pass
                    // 若是value爲false,span的class爲fail
                    span.className = value ? "pass" : "fail";

                    // 把新建的li節點添加到results下,至於這個rusults是啥?
                    // 在test執行前是body下的ul節點
                    // 在test執行後是測試組表現節點下的ul節點
                    results.appendChild(li);
                    
                    if (!value) {
                        // 若是有一個assert測試結果是false,
                        // 那麼就找到li節點的父節點的父節點,
                        // 也就是測試組表現節點了
                        var liGroup = li.parentNode.parentNode;
                        // 不能直接在測試組表現節點設置class了
                        // 必須在測試組表現節點下的span節點設置class
                        // 也就是測試組表現節點下的第一個子元素
                        liGroup.childNodes[0].className = "fail";
                    }
                    
                    // 返回li節點
                    // 在test執行前是測試組表現節點
                    // 在test執行後是assert表現節點
                    return li;
                };
                
                test = function(name, fn) {
                    // 找到document中id爲results的ul節點元素,就是那個body下的ul
                    results = document.getElementById("results");
                    // 建立一個ul節點
                    var ul = document.createElement("ul");
                    // 建立一個測試組節點,就象建立普通assert節點同樣直接調用assert
                    // 毫無心外,這個測試組節點被加到了id爲results的ul節點元素下,
                    // 初始默認這個測試組節點的測試結果是true。
                    // 在測試組節點下添加內嵌的ul節點,該測試組下的全部assert表現節點都會被
                    // 加入到這個內嵌的ul節點中。
                    // 既然如此,那麼咱們就讓results變量指向這個內嵌的ul節點
                    results = assert(true, name).appendChild(ul);

                    // 調用"測試組assert組裝函數",構建各個assert表現節點
                    fn();
                };    
            }
        </script>
    </head>
    <body>
        <ul id="results"></ul>

        <script type="text/javascript">
            asserts();

            test("數值計算測試", function() {
                assert(1 + 1 === 2, "1 + 1 = 2");
            });

            test("字符串測試", function() {
                assert("a" + "b" === "ab", '"a" + "b" = "ab"');
                assert("a" + "b" === "abc", '"a" + "b" = "abc"');
            });
        </script>
    </body>
</html>
View Code
相關文章
相關標籤/搜索