首頁 >web前端 >js教程 >使用 QUnit 測試 JavaScript 程式碼:逐步指南

使用 QUnit 測試 JavaScript 程式碼:逐步指南

王林
王林原創
2023-09-02 17:09:011024瀏覽

QUnit 由 jQuery 團隊開發,是一個用於對 JavaScript 進行單元測試的出色框架。在本教學中,我將介紹 QUnit 具體是什麼,以及為什麼您應該關心嚴格測試您的程式碼。

#什麼是 QUnit

QUnit 是一個強大的 JavaScript 單元測試框架,可以幫助您偵錯程式碼。它由 jQuery 團隊成員編寫,是 jQuery 的官方測試套件。但 QUnit 足夠通用,可以測試任何常規 JavaScript 程式碼,甚至可以透過某些 JavaScript 引擎(如 Rhino 或 V8)測試伺服器端 JavaScript。

如果您不熟悉「單元測試」的概念,請不要擔心。理解起來並不難:

在電腦程式設計中,單元測試是一種軟體驗證和確認方法,程式設計師在其中測試原始程式碼的各個單元是否適合使用。單元是應用程式最小可測試的部分。在過程式編程中,一個單元可以是一個單獨的函數或過程。

此內容引自維基百科。簡而言之,您為程式碼的每個功能編寫測試,如果所有這些測試都通過了,您可以確定程式碼將沒有錯誤(主要取決於您的測試的徹底程度)。

為什麼應該測試程式碼

如果您以前沒有編寫過任何單元測試,您可能只是將程式碼直接應用到網站上,點擊一會兒看看是否出現任何問題,並在發現問題時嘗試修復它。這種方法存在著許多問題。

首先,這非常乏味。點擊實際上並不是一件容易的事,因為你必須確保所有內容都被點擊,並且很可能你會錯過一兩件事。其次,您為測試所做的一切都不可重複使用,這意味著找到回歸並不容易。什麼是回歸?想像一下,您編寫了一些程式碼並對其進行了測試,修復了發現的所有錯誤,然後發布了它。然後,用戶發送一些有關新錯誤的回饋,並要求一些新功能。您返回程式碼,修復這些新錯誤並新增這些新功能。接下來可能發生的是一些舊的錯誤再次出現,這被稱為“回歸”。看,現在你必須再次點擊,很可能你就不會再發現這些舊的錯誤了;即使你這樣做了,你也需要一段時間才能發現問題是由回歸引起的。透過單元測試,您可以編寫測試來查找錯誤,一旦程式碼被修改,您可以再次透過測試對其進行過濾。如果出現回歸,某些測試肯定會失敗,並且您可以輕鬆發現它們,知道程式碼的哪一部分包含錯誤。由於您知道剛剛修改的內容,因此可以輕鬆修復它。

單元測試的另一個優點尤其適用於 Web 開發:它簡化了跨瀏覽器相容性的測試。只需在不同的瀏覽器上執行測試,如果一種瀏覽器出現問題,您可以修復它並再次執行這些測試,確保它不會在其他瀏覽器上引入回歸。一旦所有目標瀏覽器都通過測試,您就可以確定它們都受支援。

我想提一下 John Resig 的專案之一:TestSwarm。透過使其分佈式,它將 JavaScript 單元測試提升到一個新的水平。這是一個包含許多測試的網站,任何人都可以去那裡,運行一些測試,然後將結果返回到伺服器。這樣,程式碼就可以非常快速地在不同瀏覽器甚至不同平台上進行測試。

如何使用 QUnit 寫單元測試

那麼到底要如何使用 QUnit 來寫單元測試呢?首先需要建置測試環境:

<!DOCTYPE html>
<html>
<head>
	<title>QUnit Test Suite</title>
	<link rel="stylesheet" href="http://github.com/jquery/qunit/raw/master/qunit/qunit.css" type="text/css" media="screen">
	<script type="text/javascript" src="http://github.com/jquery/qunit/raw/master/qunit/qunit.js"></script>
	<!-- Your project file goes here -->
	<script type="text/javascript" src="myProject.js"></script>
	<!-- Your tests file goes here -->
	<script type="text/javascript" src="myTests.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>
</body>
</html>

如您所看到的,這裡使用了 QUnit 框架的託管版本。

要測試的程式碼應放入 myProject.js,並且您的測試應插入 myTests.js。要執行這些測試,只需在瀏覽器中開啟此 HTML 檔案即可。現在是時候編寫一些測試了。

單元測試的建構塊是斷言。

斷言是預測程式碼回傳結果的語句。如果預測是錯誤的,則斷言失敗,並且您知道出了問題。

要執行斷言,您應該將它們放入測試案例中:

// 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');
})

這裡我們定義了一個函數 isEven,它檢測一個數字是否為偶數,我們想測試這個函數以確保它不會回傳錯誤的答案。

我們首先呼叫test(),它建構一個測試案例;第一個參數是將在結果中顯示的字串,第二個參數是包含我們的斷言的回調函數。一旦 QUnit 運行,就會呼叫此回調函數。

我們寫了五個斷言,所有斷言都是布林值。布林斷言期望其第一個參數為 true。第二個參數也是一則會在結果中顯示的訊息。

執行測試後,您將得到以下結果:

使用 QUnit 测试 JavaScript 代码:分步指南

#

由于所有这些断言都已成功通过,我们可以非常确定 isEven() 将按预期工作。

让我们看看如果断言失败会发生什么。

// 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 does negative seven');

	// Fails
	ok(isEven(3), 'Three is an even number');
})

结果如下:

使用 QUnit 测试 JavaScript 代码:分步指南

断言失败是因为我们故意写错了,但在你自己的项目中,如果测试没有通过,并且所有断言都是正确的,你就知道发现了一个bug。

更多断言

ok() 并不是 QUnit 提供的唯一断言。在测试项目时,还有其他类型的断言很有用:

比较断言

比较断言 equals() 期望其第一个参数(即实际值)等于其第二个参数(即期望值)。它与 ok() 类似,但同时输出实际值和期望值,使调试更加容易。与 ok() 一样,它采用可选的第三个参数作为要显示的消息。

所以代替:

test('assertions', function() {
	ok( 1 == 1, 'one equals one');
})

使用 QUnit 测试 JavaScript 代码:分步指南

你应该写:

test('assertions', function() {
	equals( 1, 1, 'one equals one');
})

使用 QUnit 测试 JavaScript 代码:分步指南

注意最后一个“1”,这是比较值。

如果值不相等:

test('assertions', function() {
	equals( 2, 1, 'one equals one');
})

使用 QUnit 测试 JavaScript 代码:分步指南

它提供了更多信息,使生活变得更加轻松。

比较断言使用“==”来比较其参数,因此它不处理数组或对象比较:

test('test', function() {
	equals( {}, {}, 'fails, these are different objects');
	equals( {a: 1}, {a: 1} , 'fails');
	equals( [], [], 'fails, there are different arrays');
	equals( [1], [1], 'fails');
})

为了测试这种相等性,QUnit 提供了另一种断言:相同断言

相同的断言

相同的断言,same(),需要与 equals() 相同的参数,但它是一个深度递归比较断言,不仅适用于基本类型,还适用于数组和对象。在前面的示例中,如果将断言更改为相同的断言,它们将全部通过:

test('test', function() {
	same( {}, {}, 'passes, objects have the same content');
	same( {a: 1}, {a: 1} , 'passes');
	same( [], [], 'passes, arrays have the same content');
	same( [1], [1], 'passes');
})

请注意,same() 在可能的情况下使用“===”进行比较,因此在比较特殊值时它会派上用场:

test('test', function() {
	equals( 0, false, 'true');
	same( 0, false, 'false');
	equals( null, undefined, 'true');
	same( null, undefined, 'false');
})

构建你的断言

将所有断言放在一个测试用例中是一个非常糟糕的主意,因为它很难维护,并且不会返回干净的结果。您应该做的是构建它们,将它们放入不同的测试用例中,每个测试用例都针对单一功能。

您甚至可以通过调用模块函数将测试用例组织到不同的模块中:

module('Module A');
test('a test', function() {});
test('an another test', function() {});

module('Module B');
test('a test', function() {});
test('an another test', function() {});

使用 QUnit 测试 JavaScript 代码:分步指南

异步测试

在前面的示例中,所有断言都是同步调用的,这意味着它们依次运行。在现实世界中,还有很多异步函数,例如ajax调用或setTimeout()和setInterval()调用的函数。我们如何测试这些类型的功能? QUnit 提供了一种特殊的测试用例,称为“异步测试”,专门用于异步测试:

我们先尝试用常规的方式来写:

test('asynchronous test', function() {
	setTimeout(function() {
		ok(true);
	}, 100)
})

使用 QUnit 测试 JavaScript 代码:分步指南

看到了吗?就好像我们没有写任何断言一样。这是因为断言是异步运行的,当它被调用时,测试用例已经完成。

这是正确的版本:

test('asynchronous test', function() {
	// Pause the test first
	stop();
	
	setTimeout(function() {
		ok(true);

		// After the assertion has been called,
		// continue the test
		start();
	}, 100)
})

使用 QUnit 测试 JavaScript 代码:分步指南

在这里,我们使用 stop() 暂停测试用例,调用断言后,我们使用 start() 继续。

调用 test() 后立即调用 stop() 是很常见的;所以QUnit提供了一个快捷方式:asyncTest()。您可以像这样重写前面的示例:

asyncTest('asynchronous test', function() {
	// The test is automatically paused
	
	setTimeout(function() {
		ok(true);

		// After the assertion has been called,
		// continue the test
		start();
	}, 100)
})

有一点需要注意:setTimeout() 将始终调用其回调函数,但如果它是自定义函数(例如 ajax 调用)怎么办?您如何确定回调函数将被调用?如果不调用回调,则不会调用 start(),整个单元测试将挂起:

使用 QUnit 测试 JavaScript 代码:分步指南

所以这就是你要做的:

// A custom function
function ajax(successCallback) {
	$.ajax({
		url: 'server.php',
		success: successCallback
	});
}

test('asynchronous test', function() {
	// Pause the test, and fail it if start() isn't called after one second
	stop(1000);
	
	ajax(function() {
		// ...asynchronous assertions

		start();
	})
})

您将超时传递给 stop(),它告诉 QUnit,“如果在该超时后未调用 start(),则该测试应该失败。”您可以确信整个测试不会挂起,并且如果出现问题您将会收到通知。

多个异步函数怎么样?你把start()放在哪里?你把它放在setTimeout()中:

// A custom function
function ajax(successCallback) {
	$.ajax({
		url: 'server.php',
		success: successCallback
	});
}

test('asynchronous test', function() {
	// Pause the test
	stop();
	
	ajax(function() {
		// ...asynchronous assertions
	})

	ajax(function() {
		// ...asynchronous assertions
	})

	setTimeout(function() {
		start();
	}, 2000);
})

超时应该足够长,以允许在测试继续之前调用两个回调。但是如果其中一个回调没有被调用怎么办?你怎么知道这一点?这就是expect() 发挥作用的地方:

// A custom function
function ajax(successCallback) {
	$.ajax({
		url: 'server.php',
		success: successCallback
	});
}

test('asynchronous test', function() {
	// Pause the test
	stop();

	// Tell QUnit that you expect three assertions to run
	expect(3);

	ajax(function() {
		ok(true);
	})

	ajax(function() {
		ok(true);
		ok(true);
	})

	setTimeout(function() {
		start();
	}, 2000);
})

你向expect()传递一个数字来告诉QUnit你期望运行X个断言,如果其中一个断言没有被调用,数字将不匹配,并且你会被通知有事情发生错了。

expect() 还有一个快捷方式:只需将数字作为第二个参数传递给 test() 或 asyncTest():

// A custom function
function ajax(successCallback) {
	$.ajax({
		url: 'server.php',
		success: successCallback
	});
}

// Tell QUnit that you expect three assertion to run
test('asynchronous test', 3, function() {
	// Pause the test
	stop();

	ajax(function() {
		ok(true);
	})

	ajax(function() {
		ok(true);
		ok(true);
	})

	setTimeout(function() {
		start();
	}, 2000);
})

结论

这就是开始使用 QUnit 所需了解的全部内容。单元测试是在发布代码之前测试代码的好方法。如果您之前没有编写过任何单元测试,那么现在就开始吧!感谢您的阅读!

  • 在 Twitter 上关注我们,或订阅 Nettuts+ RSS Feed 以获取网络上最好的 Web 开发教程。

以上是使用 QUnit 測試 JavaScript 程式碼:逐步指南的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn