您的位置:首页 > 其它

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

2014-02-17 16:02 471 查看
// 直接上代码

//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" );
//...
},
//...
});
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  framework QUnit