代码改变世界

JavaScript单元测试工具使用—QUnit

2013-01-17 22:16  VVG  阅读(1815)  评论(0编辑  收藏

本文绝大部分来自于http://www.oncoding.cn/2010/javascript-unit-testing-qunit/,由于最新版的QUNIT更改了API接口,本文稍作修改。

QUnit是jQuery团队开发的JavaScript单元测试工具,使用方便,界面美观。

什么是单元测试?

单元测试又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。单元测试主要是用来检验程式的内部逻辑,也称为个体测试、结构测试或逻辑驱动测试。通常由撰写程式码的程式设计师负责进行。

通常来说,程式設計師每修改一次程式就會進行最少一次單元測試,在編寫程式的過程中前後很可能要進行多次單元測試,以證實程式達到軟件規格書(en:Specification)要求的工作目標,沒有臭蟲;雖然单元测试不是什么必须的,但也不坏,這牽涉到專案管理的政策決定。

—— 维基百科 (中文英文)

为什么JavaScript需要单元测试

由于存在浏览器解析环境、用户操作习惯等差异,前端程序的许多问题是无法捕捉或重现的,现在前端程序的测试多是黑盒测试,即靠点击点击点击来寻找程序bug。这种方式既费时费力,又无法保证测试的覆盖面。

同时,前端逻辑和交互越来越复杂,和其他编程语言一样,一个函数,一个模块,在修改bug或添加新功能的过程中,很容易就产生新的bug,或使老的bug复活。这种情况下,反复进行黑盒测试,其工作量和测试质量是可想而知的。

此外,浏览器兼容性测试是前端程序测试的重要一环,在多个浏览器之间测试前端程序,上面说的工作量就会成n倍的增加。

 

为什么我们的前端程序如此脆弱?就是因为没用单元测试。。

假如使用了单元测试,上边的问题就变得很容易了,当然前提是你要花时间去研究和编写测试用例。

根据函数或模块的源代码,编写出包含各种情况的测试用例,每次解决bug或添加新功能,都随时更新这个用例然后进行测试,很容易就找出新bug和“复活”的老bug。

测试兼容性,只需要在不同的浏览器中分别运行这个测试,问题就一目了然了。

也许白盒比黑盒要多费几倍的脑子,但想想我们那脆弱的程序,想想那些随时冒出来的烦人的老bug,费点脑子,值了!

 

使用QUnit

注:下面的内容主要参考了 QUnit文档 和 NetTuts+的这篇文章

建立一个测试页面,引入 qunit.js 和 qunit.css 这两个必需的文件,这两个文件是存放在github上的,鉴于目前操蛋的互联网环境,最好下载到本地调用。

注:body中的元素id命名必须依照如下形式,否则无法正常显示。

<!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">
  <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.11.0.css" type="text/css" media="screen" />
  <script type="text/javascript" src="http://code.jquery.com/qunit/qunit-1.11.0.js"></script>
</head>
<body>
  <h1 id="qunit-header">QUnit example</h1>
  <h2 id="qunit-banner"></h2>
  <h2 id="qunit-userAgent"></h2>
  <ol id="qunit-tests"></ol>
</body>
</html>

测试示例

下面是一个最简单的函数测试用例,解释请见程序注释。

//定义测试模块
module( "测试示例" );
//定义一个简单的函数,判断参数是不是数字
function simpleTest(para) {
  if(typeof para == "number") {
    return true;
  }
  else{
    return false;
  }
}
//开始单元测试
test('simpleTest()', function() {
  //列举各种可能的情况,注意使用 ! 保证表达式符合应该的逻辑
  ok(simpleTest(2), '2是一个数字');
  ok(!simpleTest("2"), '"2"不是一个数字');
});
    // Let's test this function
    function isEven(val) {
        return val % 2 === 0;
    }
    test('isEven()', function() {
        ok(isEven(0), 'Zero is an even number');
        ok(isEven(2), 'So is two');
        ok(isEven(-4), 'So is negative four');
        ok(!isEven(1), 'One is not an even number');
        ok(!isEven(-7), 'Neither is negative seven');
    })

module( name, [lifecycle] ) 函数指定测试模块和周期。

ok( state, [message] ) 是QUnit中最常用的一个判断函数,只能判断true和false。

DEMO在这里,看一下测试结果:

 

 结果都是绿的,说明两条测试语句都符合设定的规则。可以尝试修改下规则

//...
ok(simpleTest("2"), '"2"是一个数字');
//...

就可以看到爆红了。。

更多测试判断

除了ok()之外,QUnit还有如下几个判断函数:

相等判断equal( actual, expected, [message] )  相当于使用“==”来测试,所以equals不能用来比较数组,对象

示例:

    //定义一个简单的函数,返回数字和2的乘积
    function simpleTest1(para) {
        return para * 2;
    }
    //开始单元测试
    test('simpleTest1()', function() {
        //列举各种可能的情况
        equal(simpleTest1(2), 4, '2 * 2 等于 4');
        equal(simpleTest1(2), 3, '2 * 2 等于 3');
    });

相同判断(包含数组、对象等)使用deepEqual() 以前是使用same,最新版改为deepEqual了

示例:

    // Let's test this function
    //定义一个简单的函数,返回一个数组
    function simpleTest2() {
        return [1, 2];
    }
    //开始单元测试
    test('simpleTest2()', function() {
        //列举各种可能的情况
        deepEqual(simpleTest2(), [1, 2], '函数返回数组[1, 2]');
        deepEqual(simpleTest2(), [1, 1], '函数返回数组[1, 1]');
    });

异步与Ajax  API地址:http://api.qunitjs.com/category/async-control/

对于异步程序的测试,如setTimeout、setInterval、Ajax等情况,按照上面的方法,在异步调用执行之前,测试就已完成并输出了结果。这时,配合使用QUnit提供的两个函数:stop( [timeout] ) 和 start(),也可以轻松搞定。

直接看例子:

//异步测试
module( "异步测试示例" );
//setTimeout
test('asynchronous test', function() {
  // 暂停测试
  stop();
 
  setTimeout(function() {
    ok(true, '完成运行');
    //待测试完成后,恢复
    start();
  }, 100)
})
//另一种形式
asyncTest('asynchronous test', function() {
  setTimeout(function() {
    ok(true);
    //待测试完成后,恢复
    start();
  }, 100)
})

Ajax也是类似的道理:

//Ajax测试
function ajax(successCallback) {
  $.ajax({
    url: 'server.php',
    success: successCallback
  });
}
 
test('asynchronous test', function() {
  // 暂停测试
  stop();
 
  ajax(function() {
    // 异步调用判断
  })
 
  setTimeout(function() {
    //异步测试完成后,恢复
    start();
  }, 2000);
})

了解更多

http://api.qunitjs.com/category/assert/

http://net.tutsplus.com/tutorials/javascript-ajax/how-to-test-your-javascript-code-with-qunit/

http://www.oncoding.cn/2010/javascript-unit-testing-qunit/