一、javascript也需要单元测试吗?
这里我并不知道你有没有开发过大型的javascript项目,至今我开发过三个大型的js项目,分为是《课程节点树管理》、《在线制作试卷》和《在线聊天》。
从中我唯一的体会就是javascript的开发难度不是开发编码的过程,而是最后的测试阶段,非常的痛苦,因为你必须使用浏览器自带的调试器去调试,同时也不能自动化测试。但是后台因为用了vs自带的单元测试,所以修改起来很快。
由此我就想到javascript应该也存在单元测试,所以我搜索了一下,发现了QUnit,它的特点很明显,就是学习周期很短,可以在短时间内上手,并且也拥有非常美观的界面,所以下面我将介绍如何使用QUnit。
二、开始学习前的准备
既然要学习,我们既然就要有准备,我这里使用的是Visual Studio
2012。
下面是我的静态页面的基础模板:
<!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 5 <title></title> 6 <script type="text/javascript" src="http://code.jquery.com/qunit/qunit-1.14.0.js"></script> 7 <link type="text/css" rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.14.0.css" /> 8 </head> 9 <body> 10 <div id="qunit"></div> 11 <div id="qunit-fixture"></div> 12 <script type="text/javascript"> 13 //稍后我们会在这里写上教程 14 </script> 15 </body> 16 </html> |
这里我是直接使用CDN的路径,如果你加载CDN的资源较慢的话,我建议去下载一份放在本地,并引用,否则你的页面会奇慢无比。
三、下面我们开始学习
1.首先我们先来一个简单的测试
<script type="text/javascript"> 2 function add(a, b) { 3 return a + b; 4 } 5 6 test("Test Method 1", function () { 7 ok(add(1, 2) == 3, "Test Method 1 Pass"); 8 }); 9 </script> |
这里我进行了一个最简单的测试,测试一个函数的是不是正确,我们在测试函数中调用该函数并和我们预想的结果进行对比,如果对比失败册触发断言,下面是最终结果:
如果我们现在将上面的代码改成下面这样:
function add(a, b) { 2 return a + b + 1; 3 } 4 5 test("Test Method 1", function () { 6 ok(add(1, 2) == 3, "Test Method 1 Pass"); 7 }); |
那么最终就会触发断言,结果就是这样的:
2.如果强制指定断言的数量
有时我们可能有5个断言,但是QUnit可能最后只识别了3个其他断言仅仅是因为语句错误未执行到,这个时候我们就需要强制指定断言的数量。
假设上面的示例中应该有两个断言,代码如下改动:
function add(a, b) { 2 return a + b + 1; 3 } 4 5 test("Test Method 1", function () { 6 expect(2); 7 ok(add(1, 2) == 3, "Test Method 1 Pass"); 8 }); |
这里我们可以看到使用了 expect 这个函数,当然你也可以像下面这样写:
function add(a, b) { 2 return a + b + 1; 3 } 4 5 test("Test Method 1",2, function () { 6 ok(add(1, 2) == 3, "Test Method 1 Pass"); 7 }); |
但是QUnit官方的说明文档中已经声明了不建议使用这个方式,所以我们尽量使用前者。
下面是上面两个不同方式的结果:
我们可以清楚的看到这里报了两个错误,但实际只有一个断言没有成功。所以这样我们就实现了最终的结果。
3.异步测试
在对比较大的模块进行测试的时候,可能会出现使用定时器等耗时的测试,但是我们不能因为一个功能的耗时而导致后面的所有测试都要等待这个测试完成,所以我们就需要使用异步测试,当然如果你的模块必须是顺序的执行那么只能使用test了。
下面我们将断言语句放到一个定时器中,延迟1.5s后执行:
function add(a, b) { 2 return a + b + 1; 3 } 4 5 asyncTest("Test Method 1", function () { 6 setTimeout(function () { 7 ok(add(1, 2) == 3, "Test Method 1 Pass"); 8 start(); 9 }, 1500); 10 }); |
当你写完,刷新页面后,会发现一开始没有任何反馈的结果,但是等待一段时间后就可以看到最后的测试结果了。
这里我们可以看到我们使用了 asyncTest 这个方法,并且在断言后面加了一个
start ,如果不加start方法,那么最后你可能看到这个测试反馈的结果。
下面是结果:
4.模块化测试
时常我们会同时测试多个模块,但是如果仅仅利用前面的方式,我们会发现最终的结果会分不清那个测试是属于那个模块的,所以QUnit提供了module这个函数。
下面我们举例两个模块进行测试:
module("Test Module 1"); 2 test("Test Method1", function () { 3 ok(true, "Module1 Test1"); 4 }); 5 6 test("Test Method2", function () { 7 ok(true, "Module1 Test2"); 8 }); 9 10 module("Test Module 2"); 11 test("test Method1", function () { 12 ok(true, "Module2 Test1"); 13 }); 14 15 test("test Method2", function () { 16 ok(true, "Module2 Test2"); 17 }); |
我们可以很清楚看到 module 将不同模块的测试划分开来了,但是仅仅这里划分并没有用,我们还需要最终的结果也能够清楚的划分出来:
看到这个结果是不是很容易就可以分得出哪个方法是哪个模块的。
5.断言
断言比较多,我们举例一个,其他的简要说明下。
首先是ok这个断言,它的第一个参数仅仅只是一个表达式,第二个参数是断言的说明。
下面列出能够成立断言的几个特殊列子:
test("Test Method2", function () { 2 ok(true, "true is pass"); 3 ok(1, "1 is pass"); 4 ok(2, "2 is pass"); 5 ok(new Object(), "object is pass"); 6 ok(2 > 1, "2 > 1 is pass"); 7 ok(" ", "' ' is pass "); 8 ok([], "[] is pass"); 9 }); |
以上的断言都是可以通过的,自然相反的就不好通过了。
equal(actual,expected,message):将actual和expected进行对比(相当于==),只有相等才能通过。
notEqual(actual,expected,message):将actual和expected进行对比,只有不相等才能通过。
structEqual(actual,expected,message):将actual和expected进行全等对比(相当于===),只有相等才能通过。
notStructEqual(actual,expected,message):将actual和expected进行全等对比,只有不想等才能通过。
deepEqual(actual,expected,message):将actual和expected进行深度对比,如果是对象,则将值进行简单对比(相当于==),只有相等才能通过。
notDeepEqual(actual,expected,message):将actual和expected进行深度对比,只有不想等才能通过。
propEqual(actual,expected,message):将actual和expected进行深度对比,如果是对象,则将值进行全等对比(相当于===),只有相等才能通过。
notPropEqual(actual,expected,message):将actual和expected进行深度对比,只有不想等才能通过。
|