您的位置:首页 > Web前端 > JavaScript

使用Qunit做javascript单元测试

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

然后呢?那么然后?其实接下来就没有然后了,进度,进度,功能,功能,单元测试这东西就被抛到九霄云外了,但是自己借着这个机会了解了一下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>



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有什么区别呢?
})



通过对比结果,我们可以得到下面的结论: 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!');
})



这里执行了一个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

其他文章戳这里
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: