导读:一些开发者可能会抱怨说JavaScript难以测试,然而,随着当前对Web应用客户端重视程度的增加,JavaScript代码的单元测 试正在变得越来越重要。现在,你已有了一些可用来确保代码坚固性的工具,在本文中了解一些最常见的JavaScript单元测试工具:QUnit、YUI Test和JSTestDriver,代码例子会为你分步讲解一些示范性的测试用例。
引言
单元测试致力于验证一个模块或一个代码单元是按照设计或是如预期那样运作的。一些开发者宁愿花时间来实现一些新的模块,却把编写测试用例看成是浪费时间的事情。然而,在应付大型应用时,单元测试实际上是节省了时间;其帮助你追踪问题,让你能够安全地进行代码更新。
在 过去,单元测试只是应用在服务器端语言上。但是,随着前端组件中的复杂性的不断提升,编写JavaScript代码的测试单元的这种需要就增加了。如果你 通常都不为客户端脚本编写测试代码的话,则学习曲线可能会很陡峭,对用户界面的测试可能会要求你调整一下思维过程。(而且,一些开发者可能一时还难以认可 JavaScript是一种正式的编程语言)。
JavaScript单元测试
为了说明JavaScript的测试,本节内容分析了一个用JavaScript编写的基本函数的测试用例。清单1给出了要测试的函数:把(数值)3和传递进来的变量相加。
清单1. 源代码(example1/script.js)
双击代码全选
1
2
3
4
|
<pre class= "brush:js;toolbar:false;" > function addThreeToNumber(el){
return el + 3;
}</pre><p>
</p>
|
清单2把测试用例包含在一个自执行函数中。
清单2. 测试用例(example1/test.js)
双击代码全选
1
2
3
4
5
6
7
8
9
10
11
|
<pre class= "brush:js;toolbar:false;" >( function testAddThreeToNumber (){
var a = 5,
valueExpected= 8;
if (addThreeToNumber (a) === valueExpected) {
console.log( "Passed!" );
} else {
console.log( "Failed!" );
}
})();</pre><p>
</p>
|
在把5传入到被测试的函数中之后,测试检查其返回值是8,如果测试成功的话,在某个现代浏览器的控制台中输出Passed!,否则输出的是Failed!。为了运行这一测试,你需要:
1. 在某个充当测试运行器的HTML页面中导入两个脚本文件,如清单3所示。
2. 在浏览器中打开该页面。
清单3. HTML页面(example1/runner.html)
双击代码全选
1
2
3
4
5
6
7
8
9
10
11
|
<pre class= "brush:js;toolbar:false;" >< !DOCTYPE html>
< html>
< head>
< meta http-equiv= "Content-type" content= "text/html; charset=utf-8" >
< title>Example 1< /title>
< script type= "text/javascript" src= "js/script.js" kesrc= "js/script.js" >< /script>
< script type= "text/javascript" src= "js/test.js" kesrc= "js/test.js" >< /script>
< /head>
< body>< /body>
< /html></pre><p>
</p>
|
一种替代使用浏览器控制台的做法是,在页面中或是在由alert()方法生成的弹出窗口中输出结果。
断言(assertion),测试用例中的核心要素,被用来验证某个条件的满足。例如,在清单2中,addThreeToNumber (a) === valueExpected就是一个断言。
如果大量的测试用例都有很多断言的话,框架就派上用场了,接下来的几节内容重点说明一些最受欢迎的JavaScript单元测试框架:QUnit、YUI Test和JSTestDriver。
QUnit入门
QUnit是一个类似于JUnit(Java编程)的单元测试框架,其被JQuery团队用来测试jQuery库。若要使用QUnit,你需要:
1. 下载qunit.css和qunit.js文件(参见参考资料)。
2. 创建一个HTML页面,该页面包含了一些导入你刚下载的那些CSS和JavsScript文件的特定标签。
清单4给出了QUnit的一个标准的HTML运行器。
清单4. HTML运行器(qunit/runner.html)
双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
< !DOCTYPE html>
< html>
< head>
< meta charset= "UTF-8" />
< title>QUnit Test Suite< /title>
< link rel= "stylesheet" href= "css/qunit.css" kesrc= "css/qunit.css" type= "text/css" media= "screen" >
< script type= "text/javascript" src= "js/lib/qunit.js" kesrc= "js/lib/qunit.js" >< /script>
< /head>
< body>
< h1 id= "qunit-header" >QUnit Test Suite< /h1>
< h2 id= "qunit-banner" >< /h2>
< div id= "qunit-testrunner-toolbar" >< /div>
< h2 id= "qunit-userAgent" >< /h2>
< ol id= "qunit-tests" >< /ol>
< div id= "qunit-fixture" >test markup< /div>
< /body>
< /html>
|
假设你有两个函数负责摄氏温度到华氏温度的来回转换,清单5给出了进行这些转换的脚本。
清单5. 转换(qunit/js/script.js)
双击代码全选
1
2
3
4
5
6
7
8
9
10
|
<pre class= "brush:js;toolbar:false;" > function convertFromCelsiusToFahrenheit(c){
var f = c * (9/5) + 32;
return f;
}
function convertFromFahrenheitToCelsius(f){
var c = (f - 32) * (5/9);
return c;
}</pre><p>
</p>
|
清单6给出了其各自的测试用例。
清单6. 测试用例(qunit/js/test.js)
双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<pre class= "brush:js;toolbar:false;" >module ( "Temperature conversion" )
test( "conversion to F" , function (){
var actual1 = convertFromCelsiusToFahrenheit(20);
equal(actual1, 68, ?Value not correct?);
var actual2 = convertFromCelsiusToFahrenheit(30);
equal(actual2, 86, ?Value not correct?);
})
test( "conversion to C" , function (){
var actual1 = convertFromFahrenheitToCelsius(68);
equal(actual1, 20, ?Value not correct?);
var actual2 = convertFromFahrenheitToCelsius(86);
equal(actual2, 30, ?Value not correct?);
})</pre><p>
</p>
|
QUnit 中的测试用例由test()方法来定义,逻辑包含在传递给该函数的第二个参数中。在清单6中,这两个测试被分别命名为conversion to F和conversion to C。每个测试都包含了两个断言,这些用在测试中的断言利用了equal()方法,equal()函数让你比较预期的值和被测试的函数返回的实际值。 equal()方法中的第三个参数是在失败情况下显示的信息。
还可以通过module()函数来把一些测试组织成模块。在清单6中,模块Temperature conversion拥有两个测试。
若要运行这些测试:
1. 把源代码和测试文件放入到HTML测试器中,如清单7所示.
2. 在浏览器中打开该HTML页面。
清单7. 在运行器中包含script.js和test.js
双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<pre class= "brush:js;toolbar:false;" >...
< script type= "text/javascript" src= "js/script.js" kesrc= "js/script.js" >< /script>
< script type= "text/javascript" src= "js/test.js" kesrc= "js/test.js" >< /script>
...</pre><p>
</p>
<div style= " display:block; width:100%; padding-top:15px; margin:0px auto; height:90px; overflow:hidden; text-align:center;" ><script src= "/2011/ads/tech_content_end_468x60.js" ></script><script type= "text/javascript" ><!--
google_ad_client = "pub-5977682010997732" ;
/* 468x60, 09-9-9 */
google_ad_slot = "4048873275" ;
google_ad_width = 468;
google_ad_height = 60;
//-->
</script>
<script type= "text/javascript" src= "http://pagead2.googlesyndication.com/pagead/show_ads.js" >
</script><ins style= "display:inline-table;border:none;height:60px;margin:0;padding:0;position:relative;visibility:visible;width:468px" ><ins id= "aswift_1_anchor" style= "display:block;border:none;height:60px;margin:0;padding:0;position:relative;visibility:visible;width:468px" ><iframe allowtransparency= "true" hspace= "0" marginwidth= "0" marginheight= "0" onload= "var i=this.id,s=window.google_iframe_oncopy,H=s&&s.handlers,h=H&&H[i],w=this.contentWindow,d;try{d=w.document}catch(e){}if(h&&d&&(!d.body||!d.body.firstChild)){if(h.call){i+='.call';setTimeout(h,0)}else if(h.match){i+='.nav';w.location.replace(h)}s.log&&s.log.push(i)}" vspace= "0" id= "aswift_1" name= "aswift_1" style= "left:0;position:absolute;top:0;" scrolling= "no" width= "468" frameborder= "0" height= "60" ></iframe></ins></ins>
</div>
|
发表回复