QUnit源码阅读(2):test基本执行流程

// 直接上代码

//step 1: write a simple test as the following.
test("hello test", function() {
    ok(1 == "1", "Passed!");
});

//step 2: 调用test函数
QUnit = {
    //...
    test : function(testName, expected, callback, async) {
        //...

        //初始化test,
        test = new Test({
            name : name,
            testName : testName,
            expected : expected,
            async : async,
            callback : callback, //回调函数, very important
            module : config.currentModule,
            moduleTestEnvironment : config.currentModuleTestEnvironment,
            stack : sourceFromStacktrace(2)
        });

        if (!validTest(test)) {
            return;
        }

        test.queue();
    },
    //...
}

//step 3: 调用test.queue函数,在config.queue中存入test.init和queue的内置函数run。
Test.prototype = {
    //...
    queue : function() {
        var bad, test = this;

        synchronize(function() {
            test.init();
        });
        function run() {
            // each of these can by async
            synchronize(function() {
                test.setup();
            });
            synchronize(function() {
                test.run();
            });
            synchronize(function() {
                test.teardown();
            });
            synchronize(function() {
                test.finish();
            });
        }

        // `bad` initialized at top of scope
        // defer when previous test run passed, if storage is available
        bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-test-" + this.module + "-" + this.testName);

        if (bad) {
            run();
        } else {
            synchronize(run, true);
        }
    }
    //...
}

//step 4.1 - 4.2 在config.queue中存入callback函数
function synchronize(callback, last) {
    config.queue.push(callback);

    if (config.autorun && !config.blocking) {
        process(last);
    }
}

//step 5 调用Qunit.start函数
QUnit.load = function() {
    runLoggingCallbacks("begin", QUnit, {});
    //I do not care, right now

    //...
    QUnit.init();

    //...
    if (config.autostart) {
        QUnit.start();
    }
};



//step 6, 调用 process(true)
QUnit = {

    //...
    start : function(count) {
        config.semaphore -= count || 1;
        // don't start until equal number of stop-calls
        if (config.semaphore > 0) {
            return;
        }
        // ignore if start is called more often then stop
        if (config.semaphore < 0) {
            config.semaphore = 0;
        }
        // A slight delay, to avoid any current callbacks
        if (defined.setTimeout) {
            window.setTimeout(function() {
                if (config.semaphore > 0) {
                    return;
                }
                if (config.timeout) {
                    clearTimeout(config.timeout);
                }

                config.blocking = false;
                process(true);
            }, 13);
            //13ms后调用,即step 7
        } else {
            config.blocking = false;
            process(true);
        }
    },
    //...
} 


//step 7: 调用 process(true)
(function() {
    if (config.semaphore > 0) {
        return;
    }
    if (config.timeout) {
        clearTimeout(config.timeout);
    }

    config.blocking = false;
    process(true);
    //ref step 8
})()
//step 8, 循环泵,整个test框架的调用驱动函数,将config.queue[]中的回调依次拿出来run
//Attention: 利用函数window.setTimeout及next函数来不断循环的技巧☆☆☆
function process(last) {
    function next() {
        process(last);
    }

    var start = new Date().getTime();
    config.depth = config.depth ? config.depth + 1 : 1;

    while (config.queue.length && !config.blocking) {
        if (!defined.setTimeout || config.updateRate <= 0 || ((new Date().getTime() - start ) < config.updateRate )) {
            config.queue.shift()();
            //Attention, this is very important.
        } else {
            window.setTimeout(next, 13);
            //Or, 13ms 之后再次调用process(true).
            break;
        }
    }
    config.depth--;
    if (last && !config.blocking && !config.queue.length && config.depth === 0) {
        done();
    }
}

//step 9, 调用init函数
Test.prototype = {
    //...
    init : function() {
        //...
    },
    //...
}

//step 10. 调用run函数, 在 config.queue中放置更多的函数test.setup, run, teardown, finish
Test.prototype = {
    //...
    queue : function() {
        //...
        function run() {
            // each of these can by async
            synchronize(function() {
                test.setup();
            });
            synchronize(function() {
                test.run();
            });
            synchronize(function() {
                test.teardown();
            });
            synchronize(function() {
                test.finish();
            });
        }

        //...
    }
};

//step 11. 调用test.setup, run, teardown, finish
Test.prototype = {
    //...
    setup : function() {
        //...
    },

    run : function() {
        config.current = this;

        var running = id("qunit-testresult");

        if (running) {
            running.innerHTML = "Running: <br/>" + this.name;
        }

        if (this.async) {
            QUnit.stop();
        }

        //this.callback 将会调用我们在 step1中写的匿名函数function() { ok(1 == "1", "Passed!");})
        //this.callback 的初始化请参考step2
        if (config.notrycatch) {
            this.callback.call(this.testEnvironment, QUnit.assert);
            return;
        }

        try {
            this.callback.call(this.testEnvironment, QUnit.assert);
        } catch( e ) {
            QUnit.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + e.message, extractStacktrace(e, 0));
            // else next test will carry the responsibility
            saveGlobal();

            // Restart the tests if they're blocking
            if (config.blocking) {
                QUnit.start();
            }
        }
    },

    teardown : function() {
        //...
    },

    finish : function() {
        //...
    },
};

//step 12: 调用 我们的测试函数
(function() {
    ok(1 == "1", "Passed!");
});

//step 13: ok函数
QUnit.assert = {
    /**
     * Asserts rough true-ish result.
     * @name ok
     * @function
     * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
     */
    ok : function(result, msg) {
        //...
        result = !!result;
        //...
    },
    //...
    equal : function(actual, expected, message) {
        QUnit.push(expected == actual, actual, expected, message);
    },
}


extend( QUnit, {
    //...
    push: function( result, actual, expected, message ) {
        //...
        message = escapeInnerText( message ) || ( result ? "okay" : "failed" );
        //...        
    },
    //... 
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值