QUnit は、JavaScript の単体テストに最適なフレームワークです。このチュートリアルでは、QUnit とは正確には何なのか、そしてなぜコードを厳密にテストする必要があるのかを説明します。
QUnit は、コードのデバッグに役立つ強力な JavaScript 単体テスト フレームワークです。これは jQuery チームのメンバーによって作成され、jQuery の公式テスト スイートです。しかし、QUnit は通常の JavaScript コードをテストするのに十分な汎用性を備えており、Rhino や V8 などの特定の JavaScript エンジンを介したサーバーサイド JavaScript さえもテストできます。
「単体テスト」の概念に詳しくなくても、心配する必要はありません。理解するのは難しくありません:
コンピュータ プログラミングにおける単体テストは、プログラマーがソース コードの個々の単位が使用に適しているかどうかをテストする、ソフトウェアの検証と検証の方法です。ユニットは、アプリケーションのテスト可能な最小の部分です。手続き型プログラミングでは、ユニットは個々の関数またはプロシージャになります。
この内容はWikipediaから引用しています。つまり、コードのすべての機能に対してテストを作成し、これらのテストがすべて合格すれば、コードにバグがないことが保証されます (主にテストの徹底度に依存します)。
これまでに単体テストを作成したことがない場合は、コードを Web サイトに直接適用し、しばらくクリックして問題が発生するかどうかを確認し、問題を見つけたら修正してみてください。このアプローチには多くの問題があります。
まず第一に、それは非常に面倒です。すべてがクリックされていることを確認する必要があり、1 つや 2 つ見逃してしまう可能性があるため、クリックすることは実際には簡単な作業ではありません。第二に、テストのために行ったものはすべて再利用可能ではないため、回帰を見つけるのは簡単ではありません。回帰とは何ですか?コードを書いてテストし、見つかったバグを修正してリリースしたと想像してください。その後、ユーザーは新しいバグに関するフィードバックを送信し、いくつかの新機能をリクエストします。コードに戻り、新しいバグを修正し、新しい機能を追加します。次に起こる可能性があるのは、古いバグが再び現れることであり、これは「回帰」と呼ばれます。もう一度クリックする必要があるため、これらの古いバグはもう見つからない可能性が高く、たとえ見つかったとしても、問題の原因が回帰であることがわかるまでにはしばらく時間がかかるでしょう。単体テストを使用すると、バグを見つけるためのテストを作成でき、コードが変更されたら、テストで再度フィルタリングすることができます。リグレッションが発生した場合、一部のテストは確実に失敗しますが、コードのどの部分にエラーが含まれているかが分かるため、簡単に特定できます。何を変更したかがわかっているので、簡単に修正できます。
単体テストのもう 1 つの利点は、特に Web 開発に当てはまります。ブラウザ間の互換性のテストが簡素化されます。別のブラウザでテストを実行するだけで、あるブラウザで問題が発生した場合は、それを修正してテストを再度実行し、他のブラウザでリグレッションが発生しないことを確認できます。すべてのターゲット ブラウザがテストに合格すると、それらがすべてサポートされていることを確認できます。
John Resig のプロジェクトの 1 つである TestSwarm について触れたいと思います。分散化することで、JavaScript 単体テストを次のレベルに引き上げます。これは多くのテストを備えた Web サイトであり、誰でもそこにアクセスしていくつかのテストを実行し、結果をサーバーに返すことができます。このようにして、コードはさまざまなブラウザーやさらにはさまざまなプラットフォームにわたって非常に迅速にテストできます。
それでは、QUnit を使用して単体テストを作成するにはどうすればよいでしょうか?まず、テスト環境をセットアップする必要があります:
リーリーご覧のとおり、ここでは QUnit フレームワークのマネージド バージョンが使用されています。
テストするコードは myProject.js に配置し、テストは myTests.js に挿入する必要があります。これらのテストを実行するには、ブラウザでこの HTML ファイルを開くだけです。次に、いくつかのテストを作成します。
単体テストの構成要素はアサーションです。
アサーションは、コードによって返される結果を予測するステートメントです。予測が間違っている場合、アサーションは失敗し、何かが間違っていたことがわかります。
アサーションを実行するには、アサーションをテスト ケースに入れる必要があります:
リーリーここでは、数値が偶数かどうかを検出する関数 isEven を定義します。この関数をテストして、間違った答えが返されないことを確認したいと思います。
まず、テスト ケースを構築する test() を呼び出します。最初のパラメーターは結果に表示される文字列、2 番目のパラメーターはアサーションを含むコールバック関数です。このコールバック関数は、QUnit が実行されると呼び出されます。
5 つのアサーションを作成しましたが、それらはすべてブール値です。ブール アサーションは、最初の引数が true であることを期待します。 2 番目のパラメーターも、結果に表示されるメッセージです。
テストを実行すると、次の結果が得られます:
由于所有这些断言都已成功通过,我们可以非常确定 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 所需了解的全部内容。单元测试是在发布代码之前测试代码的好方法。如果您之前没有编写过任何单元测试,那么现在就开始吧!感谢您的阅读!
以上がQUnit を使用した JavaScript コードのテスト: ステップバイステップ ガイドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。