JavaScript 忍者秘籍笔记——利用测试和调试武装自己

第二章 利用测试和调试武装自己

调试代码

调试 javascript 有两个重要的方法:日志记录和断点

日志记录

使用console.log()方法,在控制台查看日志消息。

断点

可以在断点处随意查看任意代码的状态,包括:所有可访问的变量、上下文以及作用域链。

测试用例生成

优秀的测试用例具有三个重要特征:

  • 可重复性(repeatability)——测试结果应该是高度可再生的。多次运行测试应该产生相同的结果。
  • 简单性(simplicity)——测试应该只专注于测试一件事。在不影响测试用例目的的情况下,应尽可能消除过多的HTML标记、CSS或JavaScript。
  • 独立性(independence)——测试用例应该独立执行。避免一个测试结果依赖于另一个测试结果。

构建测试的主要方法分别是:

  • 解构型测试:在消弱代码隔离问题时进行创建,以消除任何不恰当的问题。
  • 构建型测试:从一个大家熟知的良好精简场景开始,构建用例,直到能够重现bug为止。

测试框架

根据测试需要,可以从javascript测试框架中找到很多功能,包括:

  • 能够模拟浏览器行为(单击按键等)。
  • 测试的交互式控制(暂停和恢复测试)。
  • 处理异步测试超时问题。
  • 能够过滤哪些会被执行的测试。

测试套件基础知识

测试套件的主要目的是聚合代码中的所有单个测试,将其组合成为一个单位,这样就可以批量运行,提供一个可以轻松反复运行的单一资源。

断言

单元测试框架的核心是断言方法,通常叫assert()。该方法接收一个值——需要断言的值,以及一个表示该断言目的的描述。如果该值结果为true则断言通过,否则,断言失败。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>断言的简单实现</title>
    <style>
        /*定义结果样式*/
        li.pass{color: green;}
        li.fail{color: red;}
    </style>
</head>
<body>
    <!--显示测试结果-->
    <ul id="results"></ul>

    <script>
        // 定义assert方法
        function assert(value,desc){
            var li = document.createElement('li');
            li.className = value ? "pass" : "fail";
            li.appendChild(document.createTextNode(desc));
            document.getElementById("results").appendChild(li);
        }

        // 使用断言执行测试
        window.onload = function() {
            assert(true, "测试running...");
            assert(false, "失败!");
        }
    </script>
</body>
</html>

测试组

执行单元测试时,一个测试组可能代表一组断言,因为它们在我们的API或程序里关联的是一个单一的方法。如果做行为驱动开发,测试组将通过任务集成断言。在实践中动态控制层级被证明是非常有用的(如果测试失败的话,可以收缩/展开测试组,并且过滤测试)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试分组的实现</title>
    <style>
        li.pass{color: green;}
        li.fail{color: red;}
    </style>
</head>
<body>
    <ul id="results"></ul>

    <script>
        (function(){
            var results;

            this.assert = function assert(value,desc){
                var li = document.createElement('li');
                li.className = value ? "pass" : "fail";
                li.appendChild(document.createTextNode(desc));
                results.appendChild(li);
                if(!value){
                    li.parentNode.parentNode.className = "fail";
                }
                return li;
            };

            this.test = function test(name,fn){
                results = document.getElementById("results");
                results = assert(true,name).appendChild(document.createElement("ul"));
                fn();
            };
        })();

        window.onload = function (){
            test("A test", function(){
                assert(true, "First assertion completed");
                assert(true, "Second assertion completed");
                assert(true, "Third assertion completed");
            });

            test("Another test", function(){
                assert(true, "First assertion completed");
                assert(false, "Second assertion failed");
                assert(true, "Third assertion completed");
            });

            test("A third test", function(){
                assert(null, "fail");
                assert(5, "pass");
            })
        }
    </script>
</body>
</html>

异步测试

处理该问题的方式通常是过度设计,并且设计的要比实际需要的更复杂。处理异步测试,需要遵循的简单步骤:

  1. 将依赖相同异步操作的断言组合成一个统一的测试组。
  2. 每个测试组需要放在一个队列上,在先前其他的测试组完成运行之后再运行。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>简单的异步测试套件</title>
    <style>
        li.pass{color: green;}
        li.fail{color: red;}
    </style>
</head>
<body>
    <ul id="results"></ul>

    <script>
        (function(){
            var queue = [], paused = false, results;

            // test 接收一个包含多个断言的函数,这些断言可以是同步的,也可以是异步的——并放置在队列中等待执行
            this.test = function(name,fn){
                queue.push(function(){
                    results = document.getElementById("results");
                    results = assert(true,name).appendChild(
                        document.createElement("ul"));
                        fn();
                });
                runTest();
            };

            // pause 应该在test内部调用,告诉该测试套件暂停执行测试,直到测试组完成
            this.pause = function(){
                paused = true;
            };

            // resume 恢复测试,经过延迟后开始下一个测试的运行,避免出现长时间运行的代码块
            this.resume = function(){
                paused = false;
                setTimeout(runTest,1);
            };

            // 在测试排队时从队列中移除时进行调用。用于检查当前套件目前是否没被暂停以及队列中是否有测试任务,一旦满足情况,将从队列中取出一个测试并尝试执行它
            // 测试组完成执行后,runTest 会检查该套件目前是否暂停了,如果没暂停(这意味着,测试组中只有异步测试),runTest 将开始执行下一组测试
            function runTest(){
                if(!paused && queue.length){
                    queue.shift()();
                    if(!paused){
                        resume();
                    }
                }
            }

            this.assert = function assert(value,desc){
                var li = document.createElement("li");
                li.className = value ? "pass" : "fail";
                li.appendChild(document.createTextNode(desc));
                results.appendChild(li);
                if(!value){
                    li.parentNode.parentNode.className = "fail";
                }
                return li;
            };
        })();

        window.onload = function(){
            test("Async Test #1", function(){
                pause();
                setTimeout(function(){
                    assert(true, "First test completed");
                    resume();
                },1000);
            });

            test("Async Test #2", function(){
                pause();
                setTimeout(function(){
                    assert(true, "Second test completed");
                    resume();
                },1000);
            });
        }
    </script>
</body>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容