QUnit, développé par l'équipe jQuery, est un excellent framework pour les tests unitaires JavaScript. Dans ce didacticiel, je vais vous expliquer ce qu'est exactement QUnit et pourquoi vous devriez vous soucier de tester rigoureusement votre code.
QUnit est un puissant framework de tests unitaires JavaScript qui vous aide à déboguer votre code. Il est écrit par des membres de l'équipe jQuery et constitue la suite de tests officielle pour jQuery. Mais QUnit est suffisamment général pour tester n'importe quel code JavaScript classique, et même JavaScript côté serveur via certains moteurs JavaScript comme Rhino ou V8.
Si vous n'êtes pas familier avec le concept de « tests unitaires », ne vous inquiétez pas. Ce n'est pas difficile à comprendre :
En programmation informatique, les tests unitaires sont une méthode de vérification et de validation de logiciels dans laquelle les programmeurs testent des unités individuelles de code source pour déterminer leur aptitude à l'utilisation. Une unité est la plus petite partie testable d’une application. En programmation procédurale, une unité peut être une fonction ou une procédure individuelle.
Ce contenu est cité de Wikipédia. En bref, vous écrivez des tests pour chaque fonctionnalité de votre code, et si tous ces tests réussissent, vous pouvez être sûr que votre code sera exempt de bogues (principalement en fonction de la rigueur de vos tests).
Si vous n'avez jamais écrit de tests unitaires auparavant, vous pouvez simplement appliquer le code directement sur le site Web, cliquer pendant un moment pour voir si des problèmes surviennent et essayer de le résoudre lorsque vous les trouvez. Cette approche pose de nombreux problèmes.
Tout d’abord, c’est très fastidieux. Cliquer n'est en fait pas une tâche facile car vous devez vous assurer que tout est cliqué et il y a de fortes chances que vous manquiez une chose ou deux. Deuxièmement, rien de ce que vous faites pour les tests n’est réutilisable, ce qui signifie qu’il n’est pas facile de trouver des régressions. Qu’est-ce que la régression ? Imaginez que vous ayez écrit du code, que vous l'ayez testé, que vous ayez corrigé tous les bogues que vous avez trouvés, puis que vous l'ayez publié. Les utilisateurs envoient ensuite des commentaires sur les nouveaux bugs et demandent de nouvelles fonctionnalités. Vous revenez dans le code, corrigez ces nouveaux bugs et ajoutez ces nouvelles fonctionnalités. Ce qui peut arriver ensuite, c'est que d'anciens bugs réapparaissent, c'est ce qu'on appelle une "régression". Vous voyez, maintenant que vous devez cliquer à nouveau, il y a de fortes chances que vous ne trouviez plus ces vieux bugs et même si vous le faites, il vous faudra un certain temps avant de découvrir que le problème est causé par une régression ; Avec les tests unitaires, vous pouvez écrire des tests pour trouver des bogues, et une fois le code modifié, vous pouvez le filtrer à nouveau à travers les tests. Si une régression se produit, certains tests échoueront définitivement et vous pourrez les repérer facilement, sachant quelle partie du code contient l'erreur. Puisque vous savez ce que vous venez de modifier, vous pouvez facilement le corriger.
Un autre avantage des tests unitaires s'applique particulièrement au développement Web : ils simplifient les tests de compatibilité entre navigateurs. Exécutez simplement les tests sur différents navigateurs, et si quelque chose ne va pas avec un navigateur, vous pouvez le réparer et réexécuter les tests, en vous assurant qu'il n'introduit pas de régressions sur d'autres navigateurs. Une fois que tous les navigateurs cibles réussissent le test, vous pouvez être sûr qu’ils sont tous pris en charge.
Je voudrais mentionner un des projets de John Resig : TestSwarm. En le rendant distribué, les tests unitaires JavaScript passent au niveau supérieur. C'est un site Web avec de nombreux tests, n'importe qui peut y accéder, exécuter des tests, puis renvoyer les résultats au serveur. De cette façon, le code peut être testé très rapidement sur différents navigateurs et même sur différentes plates-formes.
Alors, comment écrire des tests unitaires en utilisant QUnit ? Vous devez d'abord mettre en place un environnement de test :
<!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>
Comme vous pouvez le voir, la version gérée du framework QUnit est utilisée ici.
Le code que vous souhaitez tester doit aller dans myProject.js et vos tests doivent aller dans myTests.js. Pour exécuter ces tests, ouvrez simplement ce fichier HTML dans votre navigateur. Il est maintenant temps d'écrire quelques tests.
La pierre angulaire des tests unitaires sont les assertions.
Les assertions sont des déclarations qui prédisent ce que le code retournera. Si la prédiction est fausse, l’affirmation échoue et vous savez que quelque chose s’est mal passé.
Pour exécuter des assertions, vous devez les mettre dans des cas de test :
// 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'); })
Ici, nous avons défini une fonction isEven, qui vérifie si un nombre est pair, et nous voulons tester cette fonction pour nous assurer qu'elle ne renvoie pas la mauvaise réponse.
Nous appelons d'abord test(), qui construit un cas de test ; le premier paramètre est la chaîne qui sera affichée dans les résultats, et le deuxième paramètre est la fonction de rappel contenant notre assertion. Cette fonction de rappel est appelée une fois QUnit exécuté.
Nous avons écrit cinq assertions, toutes booléennes. Une assertion booléenne s'attend à ce que son premier argument soit vrai. Le deuxième paramètre est également un message qui sera affiché dans les résultats.
Après avoir exécuté le test, vous obtiendrez les résultats suivants :
由于所有这些断言都已成功通过,我们可以非常确定 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'); })
结果如下:
断言失败是因为我们故意写错了,但在你自己的项目中,如果测试没有通过,并且所有断言都是正确的,你就知道发现了一个bug。
ok() 并不是 QUnit 提供的唯一断言。在测试项目时,还有其他类型的断言很有用:
比较断言 equals() 期望其第一个参数(即实际值)等于其第二个参数(即期望值)。它与 ok() 类似,但同时输出实际值和期望值,使调试更加容易。与 ok() 一样,它采用可选的第三个参数作为要显示的消息。
所以代替:
test('assertions', function() { ok( 1 == 1, 'one equals one'); })
你应该写:
test('assertions', function() { equals( 1, 1, 'one equals one'); })
注意最后一个“1”,这是比较值。
如果值不相等:
test('assertions', function() { equals( 2, 1, 'one equals one'); })
它提供了更多信息,使生活变得更加轻松。
比较断言使用“==”来比较其参数,因此它不处理数组或对象比较:
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() {});
在前面的示例中,所有断言都是同步调用的,这意味着它们依次运行。在现实世界中,还有很多异步函数,例如ajax调用或setTimeout()和setInterval()调用的函数。我们如何测试这些类型的功能? QUnit 提供了一种特殊的测试用例,称为“异步测试”,专门用于异步测试:
我们先尝试用常规的方式来写:
test('asynchronous test', function() { setTimeout(function() { ok(true); }, 100) })
看到了吗?就好像我们没有写任何断言一样。这是因为断言是异步运行的,当它被调用时,测试用例已经完成。
这是正确的版本:
test('asynchronous test', function() { // Pause the test first stop(); setTimeout(function() { ok(true); // After the assertion has been called, // continue the test start(); }, 100) })
在这里,我们使用 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(),整个单元测试将挂起:
所以这就是你要做的:
// 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 所需了解的全部内容。单元测试是在发布代码之前测试代码的好方法。如果您之前没有编写过任何单元测试,那么现在就开始吧!感谢您的阅读!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!