软件测试是评估软件以检测给定输入集的预期输出与实际输出之间的差异的过程。 测试,尤其是单元测试,应该是每个开发人员生活中必不可少的一部分。 不幸的是,许多开发人员似乎对此活动感到害怕。
在JavaScript中,我们可以选择很多框架来测试我们的代码库。 Mocha , Selenium和QUnit是一些示例。 在本文中,我将向您介绍QUnit。 QUnit是由jQuery团队开发和维护的单元测试框架,该团队与jQuery和jQuery UI等项目背后的团队相同。
许多开发人员使用QUnit的主要原因之一是它的易用性。 从这个框架开始非常简单,主要概念可以在几个小时内掌握。
要使用QUnit显然要执行的第一步是下载它。 有几种方法可以做到:从网站上手动下载,使用CDN,Bower或npm。 我的建议是,除非您正在开发一个简单的实时演示,否则您不应依赖CDN来测试您的代码。 因此,请坚持使用其他选项。
对于本文,我不想设置任何先决条件(请阅读Bower和npm),因此我们将采用第一种方法。 因此,请访问QUnit网站并下载JavaScript文件(名为qunit-1.14.0.js)和CSS文件(名为qunit-1.14.0.css)的最新版本。
将它们放在一个文件夹中,您还将在其中创建一个index.html
。 在此文件中,我们将放置在网站首页中显示的HTML代码,我在下面为您的商品重复此代码。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>QUnit Example</title>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.14.0.css">
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="//code.jquery.com/qunit/qunit-1.14.0.js"></script>
<script src="tests.js"></script>
</body>
</html>
如您所见,此代码使用CDN包含CSS和JavaScript文件。 因此,您必须更新链接以包括以前下载的文件。
在标记中,您可以看到有两个<div>
。 框架使用qunit
作为其ID的第一个ID来显示其用户界面,其中显示测试结果。 第二个<div>
,其ID为qunit-fixture
,应由您(开发人员)使用。 此元素使开发人员可以测试在DOM中添加,编辑或删除元素的代码,而不必担心每次测试后都会清理DOM。 如果将代码创建的元素放在此<div>
,则QUnit将为我们进行重置。
最后,我们包含了tests.js
文件,该文件表示包含测试的文件。 我的建议是在实际项目中工作时使用文件存储测试。 在本教程的现场演示中,我使用了JSBin,当然不允许上传文件。 因此,在演示中,您将看到我已内联测试代码。
既然您已经知道了标记各部分的含义,请在浏览器中打开index.html
页面,然后看看会发生什么。
如果一切顺利,您将看到下面的实时演示所示的界面,该界面也可以作为JSBin使用 :
在此阶段,此接口对我们而言唯一相关的部分是显示QUnit在处理测试上所花费的时间,定义的断言数量以及通过和失败的测试数量的部分。 上面的演示表明我们尚未定义任何测试。 让我们修复它。
QUnit提供了两种创建新测试的方法: QUnit.test()
和QUnit.asyncTest()
。 第一个用于测试同步运行的代码,而第二个用于测试异步代码。 在本节中,我将描述如何为同步代码创建测试。
QUnit.test()
方法的签名是:
QUnit.test(name, testFunction)
第一个参数name
是一个字符串,可以帮助我们识别创建的测试。 第二个参数testFunction
是包含框架将执行的断言的函数。 框架向该函数传递一个参数,该参数公开了QUnit的所有断言方法。
将此描述放入代码中,我们可以使用以下代码更新文件tests.js
:
QUnit.test('My first test', function(assert) {
// Assertions here...
});
此代码创建一个由字符串“ My first test”标识的新测试以及一个带有空主体的函数。 在没有任何断言的情况下添加测试不是任何实用程序。 要解决此问题,我们必须学习QUnit中可用的断言方法。
断言是软件测试的核心。 它们使我们能够验证代码是否按预期工作。 在QUnit中,我们有很多方法可以验证这些期望。 可以在测试中通过传递给QUnit.test()
方法的函数的参数(在我们先前的示例中assert
QUnit.test()
来访问它们。
下面的列表总结了可用的方法,以及它们的签名和用途:
deepEqual(value, expected[, message])
:适用于所有JavaScript类型的递归严格比较。 如果value
和expected
在属性,值方面相同,并且它们具有相同的原型,则断言通过。 equal(value, expected[, message])
:使用非严格比较( ==
)验证提供的value
等于expected
参数。 notDeepEqual(value, expected[, message])
:与deepEqual()
相同,但测试不deepEqual()
; notEqual(value, expected[, message])
:与equal()
相同,但测试不平等; propEqual(value, expected[, message])
:对象的属性和值的严格比较。 如果所有属性和值都相同,则断言通过;否则,则断言。 strictEqual(value, expected[, message])
:使用严格比较( ===
)验证提供的value
等于expected
参数; notPropEqual(value, expected[, message])
:与propEqual()
相同,但测试不propEqual()
; notStrictEqual(value, expected[, message])
:与strictEqual()
相同,但测试不平等; ok(value[, message]
:如果第一个参数为真,则通过的断言; throws(function [, expected ] [, message ])
:测试回调是否抛出异常,并可选地比较抛出的错误; 这些方法接受的参数的含义如下:
value
:由函数,方法返回或存储在必须验证的变量中的值; expected
:要测试的值。 如果使用throws()
方法,则可以是错误对象(实例),错误函数(构造函数),匹配(或部分匹配)字符串表示形式的RegExp或必须返回true以传递断言的回调函数。检查;
message
:描述断言的可选字符串; function
:要执行的函数应返回错误; 现在您知道了可用的方法和参数,现在该看一些代码了。 我将尝试重现一个更实际的示例,而不是为单个函数编写多个测试。 无论如何,我将向您展示的测试应被视为完整的测试套件,但它们应使您对从何处开始有具体的了解。
为了编写提到的测试,我们需要定义一些代码进行测试。 在这种情况下,我将如下定义对象文字:
var App = {
max: function() {
var max = -Infinity;
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
},
isOdd: function(number) {
return number % 2 !== 0;
},
sortObj: function(array) {
array.sort(function(a, b) {
var date1 = new Date(a.timestamp).getTime();
var date2 = new Date(b.timestamp).getTime();
if (date1 < date2) {
return -1;
} else if (date1 === date2) {
return 0;
} else {
return 1;
}
});
}
};
如您所见,我们定义了一个包含三个函数的对象常量: max()
, isOdd()
和sortObj()
。 第一个采用任意数量的参数并返回最大值。 isOdd()
接受数字作为参数,并测试其是否为奇数。 sortObj()
接受对象数组,理想情况下应具有一个称为timestamp
的属性,并根据该属性的值对它们进行排序。
这些功能的一组可能的测试如下所示:
QUnit.test('max', function (assert) {
assert.strictEqual(App.max(), -Infinity, 'No parameters');
assert.strictEqual(App.max(3, 1, 2), 3, 'All positive numbers');
assert.strictEqual(App.max(-10, 5, 3, 99), 99, 'Positive and negative numbers');
assert.strictEqual(App.max(-14, -22, -5), -5, 'All positive numbers');
});
QUnit.test('isOdd', function (assert) {
assert.ok(App.isOdd(5), '5 is odd');
assert.ok(!App.isOdd(2), '5 is not odd');
assert.ok(!App.isOdd(0), '0 is not odd');
assert.throws(function () {
App.isOdd(null);
},
/The given argument is not a number/,
'Passing null raises an Error');
assert.throws(function () {
App.isOdd([]);
},
new Error('The given argument is not a number'),
'Passing an array raises an Error');
});
QUnit.test('sortObj', function (assert) {
var timestamp = Date.now();
var array = [{
id: 1,
timestamp: timestamp
}, {
id: 3,
timestamp: timestamp + 1000
}, {
id: 11,
timestamp: timestamp - 1000
}];
App.sortObj(array);
assert.propEqual(array, [{
id: 11,
timestamp: timestamp - 1000
}, {
id: 1,
timestamp: timestamp
}, {
id: 3,
timestamp: timestamp + 1000
}]);
assert.notPropEqual(App.sortObj(array), array, 'sortObj() does not return an array');
assert.strictEqual(App.sortObj(array), undefined, 'sortObj() returns
});
创建的第一个测试由字符串“ max”标识。 在此测试中,您可以看到四个使用strictEqual()
方法的断言。 我们使用此方法而不是equal()
因为我们希望避免以下声明通过的情况:
assert.equal(App.max(0, true), 1);
在测试内部,我们正在检查几种不同类型的输入。 我想通过此测试建议的是,尝试覆盖尽可能多的情况:无参数,所有正数,所有负数,混合情况。 我没有介绍所有的可能性,但这是一个好的开始。
第二个测试用字符串“ isOdd”标识,向您展示ok()
和throws()
的用法。 当您需要验证返回布尔值的isOdd()
例如本例中的isOdd()
函数isOdd()
时,前者非常有用。 您还可以看到运行中的throws()
方法。 使用throws()
断言中最有趣的部分可能不是第一个参数,而是引发错误的函数(在这些情况下,因为我们传递了错误的参数),而是第二个参数的变体。 实际上,我同时使用了正则表达式和Error实例。
由字符串“ sortObj”标识的第三个也是最后一个测试使其他声明方法生效。 第一个断言使用propEqual()
来验证传递给sortObj()
函数的数组是否按我们期望的顺序返回包含相同对象(相同属性和值)的数组。 在此测试中, deepEqual()
方法也很合适,因为期望的参数与输入数组相同(相同的属性,值和原型)。 我可能没有使用strictEqual()
因为它们不是同一对象,即两个指向相同内存地址的对象。
第二个断言有点天真,仅用于显示notPropEqual()
的notPropEqual()
。 这很幼稚,因为我们已经在第三个断言中使用strictEqual()
方法以更准确的方式验证了期望值。
你喜欢这个例子吗? 除了方法的签名之外,您是否学到了新的东西? 希望如此。 在结束本教程之前,还有一点要讨论。
创建测试时,最好的做法是设置我们希望执行的断言数量。 这样,如果未执行一个或多个断言,则测试将失败。 QUnit框架为此提供了Expect expect()
方法。 该方法在处理异步代码时特别有用,但是在测试同步函数时最好也使用它。 expect()
方法的签名为:
expect(assertionsNumber)
其中assertionsNumber
参数指定预期的断言数量。
有了这个新概念的知识,让我们更新测试以设置我们期望运行的断言数量:
QUnit.test('max', function(assert) {
expect(4);
// Assertions here...
});
QUnit.test('isOdd', function(assert) {
expect(5);
// Assertions here...
});
QUnit.test('sortObj', function(assert) {
expect(3);
// Assertions here...
});
下面显示了代码的实时演示,包括对expect()
的调用, 可以作为JSBin使用 。
在本教程中,我向您介绍了测试的神奇世界,尤其是如何使用QUnit对JavaScript代码进行单元测试。 我们已经看到设置QUnit框架有多么容易,以及它提供了哪些方法来测试同步功能。 此外,您还学习了框架提供的一组断言函数来测试我们的代码。 最后,我提到了设置期望运行的断言数量的重要性,以及如何使用expect()
方法设置断言的重要性。 希望您喜欢这篇文章,并考虑将QUnit集成到项目中。