Ajax- 分析JsUnit 之编写测试

来自站长百科
跳转至: 导航、​ 搜索

导航: 上一页 | ASP | PHP | JSP | HTML | CSS | XHTML | aJAX | Ruby | JAVA | XML | Python | ColdFusion

对于JsUnit,起步很简单,只需从JsUnit网站(www.edwardh.com/jsunit/)下载JsUnit zip文件。把这个压缩文件解开,会得到一个jsunit文件夹,可以把Web服务器放在这里,这样整个团队或者整个组织就能更容易地使用JsUnit。JsUnit的大部分“核心”都在jsunit/app目录中,在这里可以看到jsUnitCore.js、jsUnitTracer.js和jsUnitTestManager.js,另外还有其他一些文件。如果你想运行具体的JsUnit测试,可以使用testRunner.html来运行jsunit/tests目录中找到的任何测试页。如果你在使用IntelliJ,而且想具体使用JsUnit,jsunit/intellij目录中包含了需要的所有适当文件。

用JsUnit编写测试与用JUnit编写测试很相似。测试函数不能有任何参数,必须有一个前缀test,例如testDateValidation()。测试函数包含在一个测试页(test page)中,这类似于JUnit中的一个Test类。测试页必须包含jsUnitCore.js文件,解开JsUnit zip文件后,就会在jsunit/app目录中找到这个文件。包含这个JavaScript文件实际上就是把一个外部JavaScript文件增加到页面中;只需使用脚本元素<script language="JavaScript" src="jsUnitCore.js"></script>来引用这个文件,要记住,如果你的当前目录不是jsunit/app目录,则还需要提供jsUnitCore.js文件的相关路径信息。当然,在测试页中可以包含任意多个其他函数或JavaScript;实际上,把多个JavaScript函数放在分开的文件中,是一个很好的做法。测试函数也可以放在单独的JavaScript文件中;不过,如果这样做,就需要使用exposeTestFunctionNames()方法,这样JsUnit才能找到测试函数。实际上,如果需要针对不同的页面内容建立测试,可以把测试函数放在一个单独的文件中,这样能避免复制-粘贴问题带来的痛苦。

一般地,JsUnit会自动发现测试函数,就像JUnit会发现所有测试方法一样。不过,有些操作系统/浏览器不能合作。如果你发现不能如你所愿地发现测试函数,使用exposeTestFunctionNames()方法就能解决这个问题。

断言方法

现在你对测试函数和测试页有一定的了解了,下面需要写一些实际的测试!与用JUnit一样,你可以使用断言方法(assert method)。断言方法是任何单元测试的基本模块,它们只是一些简单的布尔表达式,可以指示一个给定语句为true还是false。断言失败时,就会产生一个错误,这样将得到众所周知的红条。与JUnit不同,JsUnit没有提供那么丰富的断言方法,但是已经足够你测试JavaScript代码了。注意,除了fail()方法的注释外,其他断言方法的注释都是可选的(这与JUnit类似,甚至也“不正确”地把可选参数放在最前面,而不是最后)。

assert([comment], booleanValue)
assertTrue([comment], booleanValue)
assertFalse([comment], booleanValue)
assertEquals([comment], value1, value2)
assertNotEquals([comment], value1, value2)
assertNull([comment], value)
assertNotNull([comment], value)
assertUndefined([comment], value)
assertNotUndefined([comment], value)
assertNaN([comment], value)
assertNotNaN([comment], value)
fail(comment)

要看这些方法怎么用于测试(象征性地从字面了解),只需看JsUnit下载包提供的测试页就行了。JsUnit还提供了一个变量:JSUNIT_UNDEFINED_VALUE,它映射到JavaScript中的undefined变量。

我们说的已经够多的了!下面来看一个简单的测试!这个例子中有一个简单的函数,会让两个数相加,而且有两个测试:一个用于正整数的相加,另一个用于负整数相加。要测试这个函数,先创建一个简单的Web页面,如代码清单6-1所示,其中包含了jsUnitCore.js文件,另外包含了要测试的函数和测试函数[4]。当然,在生产代码中,可能不能把测试代码与所测试的函数混在一起,但是作为第一次尝试这样做未尝不可。

代码清单6-1 简单的测试页

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>A Simple Test Page</title>
<script language="JavaScript" src="../jsunit/app/jsUnitCore.js"></script>
<script language="JavaScript">
function addTwoNumbers(value1, value2) {
return value1 + value2;
}
function testValidArgs() {
assertEquals("2 + 2 is 4", 4, addTwoNumbers(2, 2));
}
function testWithNegativeNumbers() {
assertEquals("negative numbers: -2 + -2 is -4", -4,
addTwoNumbers(-2, -2));
}
</script>
</head>
<body>
This is a simple test page for addTwoNumbers(value1, value2).
</body>
</html>

运行这些测试会得到图6-1所示的结果。(后面将更详细地介绍测试运行工具。)
Image0011.jpg


图6-1 简单测试的结果

显然,不太可能把生产代码(函数)与测试函数混在同一个测试页中。你可能会把生产代码放在一个单独的JavaScript文件中,然后在测试页中包含这个文件。代码清单6-2就采用了这种方法。

代码清单6-2 一种更典型的方法

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Another Test Page</title>
<script language="JavaScript" src="../jsunit/app/jsUnitCore.js"></script><n/owiki><br> <nowiki><script language="JavaScript" src="simpleJS.js"></script>
<script language="JavaScript">
function testValidArgs() {
assertEquals("2 + 2 is 4", 4, addTwoNumbers(2, 2));
}
function testWithNegativeNumbers() {
assertEquals("negative numbers: -2 + -2 is -4", -4,
addTwoNumbers(-2, -2));
}
</script>
</head>
<body>
This is a simple test page for the simpleJS file.
</body>
</html>

JavaScript文件实际上相当简单,如代码清单6-3所示。

代码清单6-3 

simple.js
function addTwoNumbers(value1, value2) {
return parseInt(value1) + parseInt(value2);
}

不出所料,结果是一样的(见图6-2)。

Image0021.jpg

图6-2 采用典型方法的结果

可以看到,两个测试函数会自动被发现,而且通常都是这样。不过,如果打开测试页,点击Run之后什么也没有发生,可能就需要使用exposeTestFunctionNames(),以确保JsUnit能找到你的测试,如代码清单6-4所示。

代码清单6-4 使用

exposeTestFunctionNames()
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>A Test Page With exposeTestFunctions</title>
<script language="JavaScript" src="../jsunit/app/jsUnitCore.js"></script>
<script language="JavaScript" src="simpleJS.js"></script>
<script language="JavaScript">
function testValidArgs() {
assertEquals("2 + 2 is 4", 4, addTwoNumbers(2, 2));
}
function testWithNegativeNumbers() {
assertEquals("negative numbers: -2 + -2 is -4", -4,
addTwoNumbers(-2, -2));
}
function exposeTestFunctionNames() {
var tests = new Array(2);
tests[0] = "testValidArgs";
tests[1] = "testWithNegativeNumbers";
return tests;
}
</script>
</head>
<body>
This is a simple test page that uses exposeTestFunctionNames.
</body>
</html>

如你所愿,这样就能工作了(见图6-3)。

如果看到如图6-4中所示的错误消息,说明你可能忘了在测试页中包含jsUnitCore.js,或者文件的路径不对。请检查测试页,再运行一次。

点击OK时,会提示你重试或取消这个测试,如图6-5所示。

Image0031.jpg

图6-3 运行使用exposeTestFunctionNames()的测试
Image0041.jpg


图6-4 JsUnit错误消息
Image0051.jpg

图6-5 重试或取消

setUp()和tearDown()

像JUnit一样,JsUnit也支持setUp()和tearDown()。JsUnit与JUnit有一点是一样的,即setUp()和tearDown()是可选的,而且setUp()会在每个测试之前调用,tearDown()会在每个测试之后调用。在大量使用setUp()和tearDown()之前,需要了解JUnit与JsUnit中setUp()和tearDown()方法的实现有两个重要区别。在JUnit中,每次测试运行会导致创建Test类的一个新实例,这说明,声明的所有实例变量在下一次测试运行时会“重置”。不过,JsUnit有所不同,它不会为每次测试运行重新加载测试页,所以变量状态会在多次测试之间保留。还有一个重要区别与测试顺序有关,使用JUnit的话,测试执行的顺序是不能保证的。在JsUnit中,测试会按测试页中声明的顺序执行,先从最上面的测试开始[5]。

代码清单6-5显示了一个相当复杂的例子,其中使用了setUp()和tearDown()方法。这里同样以前面创建的add方法为基础,但是这一次会增加一个表单。你要使用setUp()填写这个表单,然后使用tearDown()方法自行清空。

代码清单6-5 使用setUp()和tearDown()

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Using setUp and tearDown</title>
<script language="JavaScript" src="jsunit/app/jsUnitCore.js"></script>
<script language="JavaScript" src="simpleJS.js"></script>
<script language="JavaScript">
function setUp() {
document.getElementById("value1").value = "2";
document.getElementById("value2").value = "2";
}
function testValidArgs() {
assertEquals("2 + 2 should equal 4", 4, addNumbers());
}
function addNumbers() {
var val1 = document.getElementById("value1").value;
var val2 = document.getElementById("value2").value;
return addTwoNumbers(val1, val2);
}
function tearDown() {
document.getElementById("value1").value = "";
document.getElementById("value2").value = "";
}
</script>
</head>
<body>
<form id="test">
<input type="text" size="3" id="value1"/>
<input type="text" size="3" id="value2"/>
<input type="button" value="Add" onclick="addNumbers()"/>
</form>
</body>
</html>

不出意外,结果是很典型的(见图6-6)。

Image0061.jpg

图6-6 运行setUp()/tearDown()的例子

JsUnit还包含另外一个特性:setUpPage()函数,这是JUnit中所没有的。JUnit社区的有些人认为JUnit缺乏“一次性启动”和“一次性关闭”功能是设计中一个败笔。有些人则更进一步,尝试扩展JUnit来包含这个特性,或者干脆创建新的测试框架;另外,JUnit的FAQ网站甚至还介绍了一种方法来模拟这种行为[6]。大多数有关JUnit的书也讨论了解决这个问题的方法。

不过,JsUnit则不同,它确实包含了一次性启动方法;setUpPage()函数只对每个测试页调用一次,即在所有测试函数调用之前调用。现在,你可能已经发现,这里很适合完成预处理,特别是在运行测试之前如果需要向页面加载一些数据,setUpPage()函数就非常有用。不同于setUp()和tearDown()函数的是,使用setUpPage()不只是把处理放在这个函数中就行了的。如果确实选择使用这个特性,一定要保证函数完成时要把setUpPageStatus变量设置为complete,这就告诉JsUnit可以继续,接下来可以执行测试页上的测试了。

想看个例子?那好,我们再来看前面的simpleJS.js文件,再增加3个函数,补充更多的数学特性。在此包括减法、乘法和除法函数,如代码清单6-6所示。

代码清单6-6

simpleJS2.js
function addTwoNumbers(value1, value2) {
return parseInt(value1) + parseInt(value2);
}
function subtractTwoNumbers(value1, value2) {
return parseInt(value1) - parseInt(value2);
}
function multiplyTwoNumbers(value1, value2) {
return parseInt(value1) * parseInt(value2);
}
function divideTwoNumbers(value1, value2) {
return parseInt(value1) / parseInt(value2);
}

下面使用setUpPage()函数建立一些简单的测试数据,如代码清单6-7。请注意函数最后一行,必须告诉JsUnit你已经建好了测试页。

代码清单6-7 使用 setUpPage()函数

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Using setUp and tearDown</title>
<script language="JavaScript" src="jsunit/app/jsUnitCore.js"></script>
<script language="JavaScript" src="simpleJS2.js"></script>
<script language="JavaScript">
var arg1;
var arg2;
function setUpPage() {
arg1 = 2;
arg2 = 2;
setUpPageStatus = "complete";
}
function testAddValidArgs() {
assertEquals("2 + 2 should equal 4", 4, addTwoNumbers(arg1, arg2));
}
function testSubtractValidArgs() {
assertEquals("2 - 2 should equal 0", 0, subtractTwoNumbers(arg1, arg2));
}
function testMultiplyValidArgs() {
assertEquals("2 * 2 should equal 4", 4, multiplyTwoNumbers(arg1, arg2));
}
function testDivideValidArgs() {
assertEquals("2 / 2 should equal 1", 1, divideTwoNumbers(arg1, arg2));
}
</script>
</head>
<body>
This is an example of using setUpPage.
</body>
</html>

这里还是得到一个绿条(见图6-7),你可能有点厌倦了吧。


图6-7 运行setUpPage()测试
Image0071.jpg 测试集

有了一些测试页之后,你可能想把它们组织为测试集(test suite,也称测试套件),这与JUnit中的TestSuite很相似。测试集把不同的测试页分组组织,这样只需运行一个测试集就能一次运行类似的一组测试。测试集其实就是一些特殊的测试页,其中包含的测试页或其他测试集(相应地就有了一个主测试集)会按顺序运行。
可以采用定义测试页的方式来定义测试集,不过有两个例外。首先,测试集中不能包含任何测试函数;其次,你的测试集必须包含一个返回JsUnitTestSuite对象的suite()函数。可以使用两个方法向测试集中增加测试页或子测试集:addTestPage(testPage)和addTestSuite(testSuite)。前者向测试集中增加单个的测试页;后者向测试集中增加另一个测试集。要记住,向测试集中增加一个测试页时,需要提供一个完全限定名,或者要提供测试页文件相对于测试运行工具的相对路径名。换句话说,如果jsunit文件夹在测试页所在的目录下,那么测试运行工具就比你的测试更深一个层次,在下一层文件夹中。如果看到图6-8所示的一个错误,要确保提供的路径确实是相对于测试运行工具的相对路径。如果想向测试集中增加其他的测试集,要记住,提供给addTestSuite的参数类型必须为JsUnitTestSuite,它要在suite函数所在的同一个页面中声明。你可在jsunit/tests目录中看到一个测试集例子,希望对你有所帮助。代码清单6-8也显示了一个例子。


图6-8 运行一个有路径错误的测试集
Image0081.jpg 代码清单6-8 一个简单的测试集

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Sample Test Suite</title>
<script language="JavaScript" src="jsunit/app/jsUnitCore.js"></script>
<script language="JavaScript">
function sampleSuite() {
var sampleSuite = new top.jsUnitTestSuite();
sampleSuite.addTestPage("../anotherTestPage.html");
sampleSuite.addTestPage("../simpleTestPage.html");
return sampleSuite;
}
function suite() {
var testSuite = new top.jsUnitTestSuite();
testSuite.addTestSuite(sampelSuite());
testSuite.addTestPage("../setupTearDownExample.html");
return testSuite;
}
</script>
</head>
<body>
This is a simple test suite.
</body>
</html>

正如你所想,这会得到一个绿条(见图6-9)。

Image0091.jpg

图6-9 运行测试集

跟踪和日志

编写JavaScript时最困难的一部分就是跟踪代码。因为JavaScript不同于其他许多语言,没有一个得力的日志库来帮助你,因此无法以一种一致的方式打印语句,相反,你必须使用alert()。当然,alert()也不是不行,但它肯定不是最理想的方法。为了查找一个问题,要“沿路”布下一大堆alert()函数,这是很讨厌的,而且一旦修正了bug,还要再把这一大堆alert()代码统统去掉。当然,等你删除掉所有额外的alert()函数后,没准又会出现另一个bug,而且就出现在上一个bug的附近,这就要求你又得把所有alert()函数再加上。你现在应该知道,为什么没有多少人喜欢JavaScript了吧!
为了让JavaScript开发人员的日子更好过,JsUnit支持跟踪!JsUnit包含以下3个函数,任何测试都可以调用(注意,在每个函数中,value参数是可选的):

warn(message, [value])
inform(message, [value])
debug(message, [value])

JsUnit支持3个跟踪级别:warn(警告)、info(信息)和debug(调试)。运行测试时,要指定你想在哪个级别上输出。这3个级别按以下顺序层叠:warn, info, debug。这说明,如果运行测试时选择debug,就会看到warn()、inform()或debug()函数发出的所有消息。如果选择warn,则只会显示由warn()函数发出的消息,选择info则会显示由warn()和inform()发出的消息。默认值为no tracing(不跟踪)。下面向这个简单例子增加一些跟踪函数[7],来看看会发生什么(见代码清单6-9)。

代码清单6-9 向一个测试增加跟踪函数

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html> <br> <head>
<title>A Simple Test Page with Tracing</title>
<script language="JavaScript" src="jsunit/app/jsUnitCore.js"></script>
<script language="JavaScript">
function addTwoNumbers(value1, value2) {
warn("this is a warning message");
warn("this is a warning message with a value", value1);
return value1 + value2;
}
function testValidArgs() {
inform("this is an inform message");
assertEquals("2 + 2 is 4", 4, addTwoNumbers(2, 2));
}
function testWithNegativeNumbers() {
debug("this is a debug message");
assertEquals("negative numbers: -2 + -2 is -4", -4,
addTwoNumbers(-2, -2));
}
</script>
</head>
<body>
This is a simple test page for addTwoNumbers(value1, value2) with tracing.
</body>
</html>

要看跟踪函数得到的输出,需要在测试运行工具中启用跟踪,并选择适当的跟踪级别。如果选择debug,可以看到来自这3个函数的全部消息,“Close Old Trace Window on New Run”(下一次运行测试时关闭上一个跟踪窗口)复选框的作用是,如果你愿意,可以保留以前运行测试的跟踪结果(见图6-10)。


Image0101.jpg 图6-10 设置跟踪级别

Image0111.jpg 这一回不再是在测试运行工具中千篇一律地显示一个绿条了。图6-11显示了跟踪函数的输出。