使用Qunit做javascript单元测试

最近公司在搞敏捷开发,提倡拥抱变化,既然是变化,那就会出错咯,那怎么保证能及时发现代码中的错误呢?可以通过单元测试来保证代码执行的正确性,因此公司要求提交的代码必须有对应的单元测试,每次代码的提交都会跑一遍单元测试(服务端自动执行),也就是所谓的持续集成。

然后呢?那么然后?其实接下来就没有然后了,进度,进度,功能,功能,单元测试这东西就被抛到九霄云外了,但是自己借着这个机会了解了一下javascript的单元测试框架。自己主要了解了一下Qunit这个单元测试框架,类似的框架还有Jasmine,JsTestDriver,mocha,,后面可以了解几个其他的框架,货比三家才能取长补短。

Hello World开场

程序员写文章,必须用经典的Hello world开场镇楼。

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <link href="http://cdn.bootcss.com/qunit/1.18.0/qunit.min.css" rel="stylesheet">
    <script src="http://cdn.bootcss.com/qunit/1.18.0/qunit.min.js"></script>
</head>
<body>

    <div id="qunit-fixture"></div>
    <div id="qunit-header">Qunit</div>
    <div id="qunit-filteredTest"></div>
    <div id="qunit-banner"></div>
    <div id="qunit-testrunner-toolbar"></div>
    <div id="qunit-userAgent"></div>
    <ol id="qunit-tests"></ol>
    <script type="text/javascript">
        test('ok test', function(){
            ok(true, 'hello world');
        })
    </script>
</body>
</html>

enter image description here Hello world还是记忆中的味道。
我们引入了一个文件qunit.min.css以及qunit.min.js两个文件,这是Qunit所需要的js以及css。 还定义了一堆的div是用来输出结果的标记(<ol id='qunit-tests'><ol>输出测试结果)。内联js代码执行了一个test()函数,这里test()定义了一个名字为ok test的测试用例,传给test()的第二个参数为为测试真身。页面加载完毕之后就会执行test()中的第二个参数。这里执行了一个ok()方法,接受两个参数,第一个参数为boolean值,表示测试是否通过(为true则通过),第二个参数为输出信息,在测试执行通过的时候是输出。这里ok()第一个参数为true,所以测试通过输出了我们熟悉的hello world。

Qunit提供的其他断言函数

除了上面提到的ok(),还有equal(),deepEqual(),strictEqual(),throws()等等,我们来一一说明。 equal(),deepEqual(),strictEqual()这三兄弟长得很像,都带有equal,显而易见的是用来比较是否相等的断言。他们有什么区别?用例子来说话。

Equal
test('equal test', function(){
                    //true
            equal(1, 1, ' 1 equal 1');
                    //true                                                  
            deepEqual(1, 1, '1 deepEqual 1');
                    //true                                      
            strictEqual(1, 1, '1 strictEqual 1');
        //true                  
            equal(1, '1', ' 1 equal "1" '); 
                    //false                                     
            deepEqual(1, '1', '1 deepEqual "1"');   
                    //false                             
            strictEqual(1, '1', '1 strictEqual "1"');                               
                    //false
            equal({a : 1}, {a : 1}, ' {a : 1} equal {a : 1} '); 
                    //true                  
            deepEqual({a : 1}, {a : 1}, '{a : 1} deepEqual {a : 1}');
                    //false         
            strictEqual({a : 1}, {a : 1}, '{a : 1} strictEqual {a : 1}');           
            //true
        propEqual({a : {a : 1}}, {a : {a : 1}}, 'propEqual');
            //propEqual和deepEqual有什么区别呢?
        })

enter image description here 通过对比结果,我们可以得到下面的结论: euqal使用 “==” 进行比较 不能遍历对象的key进行比较,即如果比较对象不可使用equal进行比较 deepEqual 使用”===”进行比较 可以遍历对象的key进行比较 strictEqual 使用”===”进行比较, 不能遍历对象的key进行比较。 equal(), deepEqual(),strictEqual()他们几个还有几个相反意义的兄弟notEqual(), notDeepEqual(),notStrictEqual()。

throws
 test('throws', function(){
                function CustomError(message){
                    this.message = message;
                }

                CustomError.prototype.toString = function(){
                    return this.message;
                }

                throws(function(){
                    throw 'error'
                }, "throw with just a message");

                throws(function(){
                    throw new CustomError();
                }, CustomError, 'raised error is an instance of CustomError');

                throws(function(){
                    throw new CustomError('test')
                }, new CustomError('test'), 'raised error instance matches the CustomError instance');

                throws(function(){
                    throw new CustomError('test')
                }, function(error){
                    return error.message == 'test';
                }, 'raised error instance satisfies the callback function');
            })

throws()接受三个参数throws(block, expected, message)

  • block需要测试的函数
  • expected : Error Object (instance), Error Function (constructor), a RegExp that matches (or partially matches) the String //representation, or a callback Function that must return true to pass the assertion check。
  • message:输出信息
module()

如果多个测试项,都需要执行相同的初始化步骤,我们可以把这些测试项放到同一个module下面。

module('module test', {
        setup : function(){
            console.log('module setup');
        },
        teardown : function(){
            console.log('module teardown');
        }
    });

    test('just a simple test in module test', function(){
        ok(true, 'i am ok');

    });

    test('just a simple test in module test2', function(){
        equal(1, 1, 'i am equal');
    })

这个例子没啥意义,纯粹为了示例而示例,我们把控制台代开,可以看到’module setup’和’module teardown’分别被打印了两次。每个用例执行之前都会执行’setup()’,每个用例执行完成之后会执行teardown()方法。我们可以在setup以及teardown里面来做一些初始化的操作。

测试异步代码

代码是同步的时候,Qunit的单元测试从上到下执行不会有顺序的问题,但是碰到异步执行的javascript代码的时候,就需要告诉Qunit你先给我停住,我让你执行的时候你再执行。

test('async', function(){
            var a = '';
            setTimeout(function(){
                a = 1;  
                equal(1, 1, 'i am equal');
            }, 1000);

        })

        test('i am just a simple test', function(){
            ok(true, 'are you ok!');
        })

enter image description here 这里执行了一个setTimeout函数,test函数执行完之后,equal断言并没有执行,因此给了我们一个Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.错误提示。

test('async', function(){
        var a = '';
        stop();
        setTimeout(function(){
            a = 1;  
            start();
            equal(1, 1, 'i am equal');
        }, 1000);

    })

相比上面的例子,在执行setTimeout之前,调用了一个stop函数告诉Qunit暂停执行测试,start()告诉Qunit恢复执行测试。

#####**asyncTest**
    asyncTest('asyncTest2', function(){
        expect(2);

        setTimeout(function(){
            ok(true, "asyncTest2");
            ok(true, 'asyncTest2');
            start();
        }, 1000);
    })

注意到上面的例子并没有stop()木有,注意到asyncTest()替代了test()木有。asyncTest告诉Qunit这是一个异步测试,所以不需要调用stop()
expect(num)告诉qunit这个测试用例中预期执行num个用例,如果不够数或者多了就给你打大红叉叉。

自定义断言

Qunit给我们提供了一些常用的断言,ok(),equal()等等,一些特殊业务我们需要自定义断言,qunit也是支持的。

//自定义断言,QUnit.push
    function addNumEqual(num, step, result, message){
        var expected = num + step;
        var actual = result;

        QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
    }

    test('self define assert method', function(){
        addNumEqual(1,2,4,'wrong');
        addNumEqual(1,2,3, 'right');
    })

自定义断言其实就是一个普通的函数,addNumEqual接受四个参数,前两个相加和第三个对比,第四个为输出信息。

上面的例子戳这里Qunit DEMO

其他文章戳这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值