Qunit的使用

水之原發表於2013-08-02

1.新建一個html頁面,如下

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Collection Test</title>
        <link rel="stylesheet" href="SlickGrid/lib/qunit.css" type="text/css"/>
        <script type="text/javascript" src="SlickGrid/lib/qunit.js">
        </script>
        <script type="text/javascript" src="collections.js">
        </script>
        <script type="text/javascript" src="ArrayListTest.js">
        </script>
        <script type="text/javascript" src="MapTest.js">
        </script>
    </head>
    <body>
        <h1 id="qunit-header">ArrayList And Map Test</h1>
        <h2 id="qunit-banner"></h2>
        <h2 id="qunit-userAgent"></h2>
        <ol id="qunit-tests">
        </ol>
        <div id="qunit-fixture">
        </div>
    </body>
</html>

在頁面中引入qunit.js和qunit.css, 可以去官網下載:http://qunitjs.com/

引入你要測試的js

// 封裝ArrayList類,用法與java類似。
(function(win){
    var ArrayList = function(array){
        var datas = [];
        
        this.setDatas = function(value){
            datas = value;
        };
        
        this.getDatas = function(){
            return datas;
        };
        
        if (array && Object.prototype.toString.call(array) === "[object Array]") {
            this.setDatas(array);
        }
    };
    
    var proto = ArrayList.prototype;
    
    // ArrayList的大小
    proto.size = function(){
        return this.getDatas().length;
    };
    
    // 判斷是否為空
    proto.isEmpty = function(){
        return this.size() === 0;
    };
    
    // 判斷是否包含指定的值
    proto.contains = function(value){
        return this.indexOf(value) !== -1;
    };
    
    // 查詢指定的值,如果找到,返回它所在的陣列下標
    proto.indexOf = function(value){
        var datas = this.getDatas();
        
        for (var index = 0, len = datas.length; index < len; index++) {
            if (datas[index] === value) {
                return index;
            }
        }
        
        return -1;
    };
    
    // 從後往前找指定的值
    proto.lastIndexOf = function(value){
        var datas = this.getDatas();
        
        for (var index = datas.length - 1; index >= 0; index--) {
            if (datas[index] === value) {
                return index;
            }
        }
        
        return -1;
    };
    
    // 把ArrayList轉化為陣列
    proto.toArray = function(){
        return this.getDatas();
    };
    
    // 判斷是否超出範圍
    proto.outOfBound = function(index){
        return index < 0 || index > (this.size() - 1);
    };
    
    // 根據位置取得指定的值
    proto.get = function(index){
        var datas = this.getDatas();
        
        if (this.outOfBound(index)) {
            return null;
        }
        
        return datas[index];
    };
    
    // 根據位置設定指定的值
    proto.set = function(index, value){
        var datas = this.getDatas();
        datas[index] = value;
    };
    
    // 增加指定的值
    proto.add = function(value){
        var datas = this.getDatas();
        datas.push(value);
    };
    
    // 在指定的位置插入值
    proto.insert = function(index, value){
        var datas = this.getDatas();
        
        if (this.outOfBound(index)) {
            return false;
        }
        
        datas.splice(index, 0, value);
        return true;
    };
    
    // 刪除指定位置的值
    proto.remove = function(index){
        var datas = this.getDatas();
        
        if (this.outOfBound(index)) {
            return false;
        }
        
        datas.splice(index, 1);
        return true;
    };
    
    // 刪除指定的值
    proto.removeValue = function(value){
        if (this.contains(value)) {
            this.remove(this.indexOf(value));
            return true;
        }
        return false;
    };
    
    // 清空ArrayList
    proto.clear = function(){
        this.getDatas().length = 0;
    };
    
    // 把另一個ArrayList新增到當前ArrayList中
    proto.addAll = function(list){
        if (!list instanceof ArrayList) {
            return false;
        }
        
        var datas = list.getDatas();
        for (var index = 0, len = datas.length; index < len; index++) {
            this.add(list.get(index));
        }
        
        return true;
    };
    
    // 在指定位置插入一個ArrayList
    proto.insertAll = function(index, list){
        if (this.outOfBound(index)) {
            return false;
        }
        
        if (!list instanceof ArrayList) {
            return false;
        }
        
        var pos = index;
        var datas = list.getDatas();
        for (index = 0, len = datas.length; index < datas.length; index++) {
            this.insert(pos++, list.get(index));
        }
        
        return true;
    };
    
    // 按數字排序
    function numberorder(a, b){
        return a - b;
    }
    
    // 排序,isNumber為true表示按數字排序
    proto.sort = function(isNumber){
        var datas = this.getDatas();
        
        if (isNumber) {
            datas.sort(numberorder);
        }
        else {
            datas.sort();
        }
    };
    
    // 重寫toString方法
    proto.toString = function(){
        var datas = this.getDatas();
        return "[" + datas.join() + "]";
    };
    
    // 重寫valueOf方法
    proto.valueOf = function(){
        return this.toString();
    };
    
    win.ArrayList = ArrayList;
})(window);

// 封裝map類,用法與java類似
(function(win){
    var Map = function(obj){
        var count = 0;
        var entrySet = {};
        
        this.setCount = function(value){
            count = value;
        };
        
        this.getCount = function(){
            return count;
        };
        
        this.setEntrySet = function(value){
            entrySet = value;
        };
        
        this.getEntrySet = function(){
            return entrySet;
        };
        
        if (obj && Object.prototype.toString.call(obj) === "[object Object]") {
            this.setEntrySet(obj);
            
            var key = null;
            for (key in obj) {
                count++;
            }
        }
    };
    
    var proto = Map.prototype;
    
    // map的大小
    proto.size = function(){
        return this.getCount();
    };
    
    // 判斷map是否為空
    proto.isEmpty = function(){
        return this.size() === 0;
    };
    
    // 判斷map是否包含指定的key
    proto.containsKey = function(key){
        if (this.isEmpty()) {
            return false;
        }
        
        var entrySet = this.getEntrySet();
        return entrySet.hasOwnProperty(key);
    };
    
    // 判斷map是否包含指定的值
    proto.containsValue = function(value){
        var entrySet = this.getEntrySet();
        
        if (this.isEmpty()) {
            return false;
        }
        
        var key = null;
        for (key in entrySet) {
            if (entrySet[key] === value) {
                return true;
            }
        }
        
        return false;
    };
    
    // 根據key取得相應的value
    proto.get = function(key){
        var entrySet = this.getEntrySet();
        
        if (this.isEmpty()) {
            return null;
        }
        
        if (this.containsKey(key)) {
            return entrySet[key];
        }
        
        return null;
    };
    // 把鍵值對放入到map中
    proto.put = function(key, value){
        var entrySet = this.getEntrySet();
        
        if (!entrySet.hasOwnProperty(key)) {
            var count = this.getCount();
            this.setCount(count + 1);
        }
        
        entrySet[key] = value;
        this.setEntrySet(entrySet);
    };
    
    // 移除指定鍵值對
    proto.remove = function(key){
        if (this.isEmpty()) {
            return false;
        }
        
        if (this.containsKey(key)) {
            var entrySet = this.getEntrySet();
            var count = this.getCount();
            delete entrySet[key];
            count--;
            this.setCount(count);
            this.setEntrySet(entrySet);
            return true;
        }
        else {
            return false;
        }
    };
    
    // 把指定map放入到當前map中
    proto.putAll = function(map){
        if (!map instanceof Map) {
            return false;
        }
        
        var entrySet = map.getEntrySet();
        
        var key = null;
        for (key in entrySet) {
            this.put(key, entrySet[key]);
        }
        
        return true;
    };
    
    // 清除當前map
    proto.clear = function(){
        this.setEntrySet({});
        this.setCount(0);
    };
    
    // 取得當前map中的所有值,返回 一個陣列
    proto.values = function(){
        var result = [];
        
        var entrySet = this.getEntrySet();
        var key = null;
        for (key in entrySet) {
            result.push(entrySet[key]);
        }
        
        return result;
    };
    
    // 取得當前map中的所有鍵,返回一個陣列
    proto.keySet = function(){
        var result = [];
        
        var entrySet = this.getEntrySet();
        var key = null;
        for (key in entrySet) {
            result.push(key);
        }
        
        return result;
    };
    
    proto.entrySet = function(){
        return this.getEntrySet();
    };
    
    // 重寫toString方法
    proto.toString = function(){
        var result = [];
        
        var entrySet = this.getEntrySet();
        var key = null;
        for (key in entrySet) {
            result.push(key + ":" + entrySet[key]);
        }
        
        return "{" + result.join() + "}";
    };
    
    // 重寫valueOf方法
    proto.valueOf = function(){
        return this.toString();
    };
    
    win.Map = Map;
})(window);

 

引入你將要編寫的測試的js

body裡的內容固定如上圖。

2.寫自己的測試程式碼

(function(){
    var list = null;
    
    module("ArrayList", {
        setup: function(){
            list = new ArrayList();
        },
        teardown: function(){
            list = null;
        }
    });
    
    test("constructor", function(){
        ok(list.isEmpty(), "new ArrayList() is Empty.");
        list = new ArrayList([1, 2, 3, 4]);
        equals(4, list.size(), "new ArrayList([1,2,3,4]), list.size()");
        list = new ArrayList(null);
        equals(0, list.size(), "new ArrayList(null), list.size()");
    });
    
    test("setDatas", function(){
        list.setDatas([8, 6, 3, 7, 9]);
        equals(5, list.size(), "list.setDatas([8,6,3,7,9]); list.size()");
        equals("[8,6,3,7,9]", list.toString(), "list.toString()");
    });
    
    test("getDatas", function(){
        list.setDatas([8, 6, 3, 7, 9]);
        equals(5, list.getDatas().length, "list.setDatas([8,6,3,7,9]); list.getDatas().length");
        equals("8,6,3,7,9", list.getDatas().toString(), "list.getDatas().toString()");
    });
    
    test("size", function(){
        equals(0, list.size(), "only call constructor, list.size()");
        list.add(1);
        list.add(2);
        list.add(3);
        equals(3, list.size(), "call add method three times, list.size()");
    });
    
    test("isEmpty", function(){
        equals(true, list.isEmpty(), "only call constructor, list.isEmpty()");
        list.add(1);
        equals(false, list.isEmpty(), "call add method one times, list.isEmpty()");
    });
    
    test("contains", function(){
        list.add(1);
        list.add(2);
        equals(true, list.contains(1), "list is [1,2], list.contains(1)");
        equals(true, list.contains(2), "list is [1,2], list.contains(2)");
        equals(false, list.contains(3), "list is [1,2], list.contains(3)");
    });
    
    test("indexOf", function(){
        list.add(1);
        list.add(2);
        equals(0, list.indexOf(1), "list is [1,2], list.indexOf(1)");
        equals(1, list.indexOf(2), "list is [1,2], list.indexOf(2)");
        equals(-1, list.indexOf(3), "list is [1,2], list.indexOf(3)");
    });
    
    test("lastIndexOf", function(){
        list.add(1);
        list.add(2);
        equals(0, list.lastIndexOf(1), "list is [1,2], list.lastIndexOf(1)");
        equals(1, list.lastIndexOf(2), "list is [1,2], list.lastIndexOf(2)");
        equals(-1, list.lastIndexOf(3), "list is [1,2], list.lastIndexOf(3)");
    });
    
    test("toArray", function(){
        list.add(1);
        list.add(2);
        equals("1,2", list.toArray().toString(), "list is [1,2], list.toArray() is");
        same(list.getDatas(), list.toArray());
    });
    
    test("outOfBound", function(){
        list.add(1);
        list.add(2);
        same(true, list.outOfBound(-1), "list is [1,2], list.outOfBound(-1) is");
        same(false, list.outOfBound(0), "list is [1,2], list.outOfBound(0) is");
        same(false, list.outOfBound(1), "list is [1,2], list.outOfBound(1) is");
        same(true, list.outOfBound(2), "list is [1,2], list.outOfBound(2) is");
    });
    
    test("get", function(){
        list.add("jack");
        list.add("mary");
        same("jack", list.get(0), "list is [jack, mary], list.get(0) is");
        same("mary", list.get(1), "list is [jack, mary], list.get(1) is");
    });
    
    test("set", function(){
        list.add("jack");
        list.set(1, "mary");
        same("mary", list.get(1), 'list.set(1, "mary"); list.get(1) is');
        list.set(0, "mike");
        same("mike", list.get(0), 'list.set(0, "mike"); list.get(0) is');
    });
    
    test("add", function(){
        list.add(10);
        list.add(20);
        equal(10, list.get(0), "list add after is [10,20], list.get(0) is");
        equal(20, list.get(1), "list add after is [10,20], list.get(1) is");
    });
    
    test("insert", function(){
        list.add(10);
        list.add(20);
        list.insert(0, 30);
        equal(30, list.get(0), "list is [10,20], list.insert(0, 30), list.get(0) is");
        list.insert(2, 6);
        equal(6, list.get(2), "list is [30,10,20], list.insert(2, 6), list.get(2) is");
    });
    
    test("remove", function(){
        list.add(1);
        list.add(2);
        equal(true, list.remove(0), "list is [1, 2], list.remove(0) is ");
        list.clear();
        equal(false, list.remove(0), "list is [], list.remove(0) is ");
    });
    
    test("removeValue", function(){
        list.add("jack");
        list.add("mike");
        equal(true, list.removeValue("jack"), "list is ['jack', 'mike'], list.removeValue('jack') is");
        list.clear();
        equal(false, list.removeValue("jack"), "list is [], list.removeValue('jack') is");
    });
    
    test("clear", function(){
        list.add(10);
        list.add(20);
        ok(!list.isEmpty(), "Before call clear, list is " + list.toString());
        list.clear();
        ok(list.isEmpty(), "After call clear, list is " + list.toString());
    });
    
    test("addAll", function(){
        var tmpList = new ArrayList([10, 20]);
        list.addAll(tmpList);
        same(list.getDatas(), tmpList.getDatas(), "tmpList is [10,20], list.addAll(tmpList), list is ");
    });
    
    test("insertAll", function(){
        var tmpList = new ArrayList([10, 20]);
        list.add(30);
        list.insertAll(0, tmpList);
        same(10, list.get(0), "tmpList is [10,20], list is [30], list.insertAll(0, tmpList), list.get(0) is ");
        same(20, list.get(1), "tmpList is [10,20], list is [30], list.insertAll(0, tmpList), list.get(1) is ");
        same(30, list.get(2), "tmpList is [10,20], list is [30], list.insertAll(0, tmpList), list.get(2) is ");
        same(3, list.size(), "list.size() is ");
    });
    
    test("sort", function(){
        list.setDatas([6, 9, 3, 1, 5]);
        list.sort(true);
        same("[1,3,5,6,9]", list.toString(), "list is [6,9,3,1,5], list.sort(true), list is ");
        list.clear();
        list.setDatas([4, 33, 222, 1111]);
        list.sort();
        same("[1111,222,33,4]", list.toString(), "list is [4,33,222,1111], list.sort(), list is ");
    });
    
    test("toString", function(){
        list.setDatas([6, 9, 3]);
        same("[6,9,3]", list.toString(), "list is [6, 9, 3], list.toString() is ");
    });
    
    test("valueOf", function(){
        list.setDatas([6, 9, 3]);
        same("[6,9,3]", list.valueOf(), "list is [6, 9, 3], list.toString() is ");
    });
})();
(function(){
    var map = null;
    
    module("Map", {
        setup: function(){
            map = new Map();
            map.put("v001", "jack");
            map.put("v002", "mike");
        },
        teardown: function(){
            map = null;
        }
    });
    
    test("constructor", function(){
        map = new Map();
        ok(map.isEmpty(), "only call constructor, map is empty.");
        map = new Map({
            'v001': 'jack',
            'v002': 'mike'
        });
        equal("jack", map.get('v001'), "new Map({'v001' : 'jack', 'v002': 'mike'}), map.get('v001') is");
        equal("mike", map.get('v002'), "new Map({'v001' : 'jack', 'v002': 'mike'}), map.get('v002') is");
    });
    
    test("put size", function(){
        equal(2, map.size(), "call put two times, the key not same, map.size() is");
        map.put("v002", "lucy");
        equal(2, map.size(), "call put three times , the last time, the key is repeat, map.size() is");
    });
    
    test("isEmpty", function(){
        equal(false, map.isEmpty(), "call put two times, map.isEmpty() is");
    });
    
    test("containsKey", function(){
        equal(true, map.containsKey("v001"), "map is {'v001' : 'jack', 'v002': 'mike'}, map.containsKey('v001') is");
        equal(true, map.containsKey("v002"), "map is {'v001' : 'jack', 'v002': 'mike'}, map.containsKey('v002') is");
        equal(false, map.containsKey("v003"), "map is {'v001' : 'jack', 'v002': 'mike'}, map.containsKey('v003') is");
    });
    
    test("containsValue", function(){
        equal(true, map.containsValue("jack"), "map is {'v001' : 'jack', 'v002': 'mike'}, map.containsValue('jack') is");
        equal(true, map.containsValue("mike"), "map is {'v001' : 'jack', 'v002': 'mike'}, map.containsValue('mike') is");
        equal(false, map.containsValue("lucy"), "map is {'v001' : 'jack', 'v002': 'mike'}, map.containsValue('lucy') is");
    });
    
    test("get", function(){
        equal("jack", map.get("v001"), "map is {'v001' : 'jack', 'v002': 'mike'}, map.get('v001') is");
        equal("mike", map.get("v002"), "map is {'v001' : 'jack', 'v002': 'mike'}, map.get('v002') is");
    });
    
    test("remove", function(){
        equal(true, map.remove("v001"), "map is {'v001' : 'jack', 'v002': 'mike'}, map.remove('v001') is");
        equal(false, map.remove("v003"), "map is {'v001' : 'jack', 'v002': 'mike'}, map.remove('v003') is");
    });
    
    test("putAll", function(){
        var map2 = new Map();
        map2.putAll(map);
        equal(map.size(), map2.size(), "map2 is empty, map2.putAll(map), map2.size() is ");
    });
    
    test("clear", function(){
        equal(2, map.size(), "before call clear method, map.size() is ");
        map.clear();
        equal(0, map.size(), "after call clear method, map.size() is ");
    });
    
    test("values", function(){
        equal('jack,mike', map.values().join(), "map is {'v001' : 'jack', 'v002': 'mike'}, map.values() is ");
    });
    
    test("keySet", function(){
        equal('v001,v002', map.keySet().join(), "map is {'v001' : 'jack', 'v002': 'mike'}, map.keySet() is ");
    });
    
    test("entrySet", function(){
        var map2 = new Map(map.entrySet());
        equal('{v001:jack,v002:mike}', map2.toString(), "map is {'v001' : 'jack', 'v002': 'mike'}, map.entrySet() is ");
    });
    
    test("toString", function(){
        equal('{v001:jack,v002:mike}', map.toString(), "map is {'v001' : 'jack', 'v002': 'mike'}, map.toString() is ");
    });
    
    test("valueOf", function(){
        equal('{v001:jack,v002:mike}', map.valueOf(), "map is {'v001' : 'jack', 'v002': 'mike'}, map.valueOf() is ");
    });
})();

module有兩個主要的方法:setup和teardown, 分別是在方法呼叫前和呼叫後執行。

test("", function(){}); 表示執行一個測試用例

用於診斷的方法主要有:equals、equal、same、deepEqual、notEqual、notDeepEqual、strictEqual、notStrictEqual、ok、expect

詳細用法見官方文件。