JavaScript 基础与提高

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

2017-02-06  本文已影响23人  soojade

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

调试代码

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

日志记录

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

断点

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

测试用例生成

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

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

测试框架

根据测试需要,可以从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>
上一篇下一篇

猜你喜欢

热点阅读