@ledsun blog

無味の味は佳境に入らざればすなわち知れず

QUnitの概要

QUnitとは

JavaScript用のTestingFrameworkです。

由来

jQueryのテスト用に作られ、今もjQueryjQuery-UIのテストに使われています。 それ自体はjQueryに依存していませんが、テスト内でDOMを操作したりイベントを生成したりするのにjQueryを使うと便利です。

JavaScript的な側面

実行環境

QUnitとテスト用のスクリプトをブラウザに読み込んで実行します。 テスト結果はそのままブラウザに表示します。

他のツールと連携すると

  • PhantomJSを使えばCLIで実行可能
  • Testemを使うとテスト修正時の自動リロード(再実行)が可能
  • Gruntからもgrunt-contrib-qunitプラグインを使って実行可能

ちなみに、mochaというTestingFrameworkはnode.jsで実行します。

インストール

npmで入れることもできますが、scriptタグでブラウザに読み込むのでソースをローカルに置くか、CDNから取得するのが一般的です。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>QUnit Example</title>
  <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.12.0.css">
</head>
<body>
  <div id="qunit"></div>
  <div id="qunit-fixture"></div>
  <script src="http://code.jquery.com/qunit/qunit-1.12.0.js"></script>
  <script src="tests.js"></script>
</body>
</html>

test.jsはこのような内容です。

test("hello test", function() {
  ok(1 == "1", "passed!");
});

TestingFramewokとしての側面

xUnitの一種である。テストケースとアサートでテストを記述する。 ちなみに、JasmineというTestingFrameworkはBDD形式で記述します。

テストケース

テストケースはtestという関数で記述します。

test("hello test", function() {
    ok(1 == "1", "passed!");
});

第一引数がテストケース名で第二引数に内容を書きます。

アサート

アサート用の関数は3種類あります。

  1. ok
  2. equal
  3. throws
OK

真であるか判定します。

test("ok test", function() {
    ok(true, "true succeeds");
    ok("non-empty", "non-empty string succeeds");

    ok(false, "false fails");
    ok(0, "0 fails");
    ok(NaN, "NaN fails");
    ok("", "empty string fails");
    ok(null, "null fails");
    ok(undefined, "undefined fails");
});
equal

二つの値が同じであるか判定します。

test("equal test", function() {
    equal(0, 0, "Zero, Zero; equal succeeds");
    equal("", 0, "Empty, Zero; equal succeeds");
    equal("", "", "Empty, Empty; equal succeeds");

    equal("three", 3, "Three, 3; equal fails");
    equal(null, false, "null, false; equal fails");
});

equalは==で比較するため型変換されます。===で比較するstrictEqual関数もあります。

test("strict equal test", function() {
    strictEqual(0, 0, "Zero, Zero; strict equal succeeds");

    strictEqual("", 0, "Empty, Zero; strict equal failss");
    strictEqual("0", 0, "String Zero, Number Zero; strict equal failss");
});

オブジェクトや配列の中身を比較するdeepEqualもあります。

test("deepEqual test", function() {
    var obj = {
        foo: "bar"
    };

    deepEqual(obj, {
        foo: "bar"
    }, "Two objects can be the same in value");
});

それぞれ異なることを判定するための関数があります。

  • notEqual
  • notStrictEqual
  • notDeepEqual
throws

例外が出ることを確認します。

test("throws", function() {
    throws(
        function() {
            throw "error"
        },
        "throws with just a message, no expected"
    );
});

Callbackのテスト

Callback関数が呼ばれるかどうかをテストする方法があります。

同期コールバック

イベントハンドラーなどの即座に呼び出されるコールバックの確認にはexpect関数を使います。

test("a test", function() {
  expect(1);
  var $body = $("body");

  $body.on("click", function() {
    ok(true, "body was clicked!");
  });

  $body.trigger("click");
});

expectの引数は想定する、okなどアサート関数の呼び出し回数です。 test関数の第二引数に指定することもできます。

test("a test", 1, function() {
  var $body = $("body");

  $body.on("click", function() {
    ok(true, "body was clicked!");
  });

  $body.trigger("click");
});
非同期コールバック

Ajaxなどの即座に帰ってこないコールバックはコールバックの呼び出しを待つことができます。 このときはtest関数の代わりにasyncTest関数を使います。

asyncTest("asynchronous test: video ready to play", 1, function() {
  var $video = $("video");

  $video.on("canplaythrough", function() {
    ok(true, "video has loaded and is ready to play");
    start();
  });
});

テストの実行を止め、start関数が呼ばれるまで待ちます。

テストケースのグループ化

module関数を使うとテストケースをグループ化できます。

module("group a");
test("a basic test example", function() {
  ok(true, "this test is fine");
});
test("a basic test example 2", function() {
  ok(true, "this test is fine");
});

module("group b");
test("a basic test example 3", function() {
  ok(true, "this test is fine");
});
test("a basic test example 4", function() {
  ok(true, "this test is fine");
});

モジュールにはsetup/teadown関数を指定してできます。これらの関数は各テストの実行前後に呼び出されます。

module("module", {
  setup: function() {
    ok(true, "one extra assert per test");
  },
  teardown: function() {
    ok(true, "and one extra assert after each test");
  }
});
test("test with setup and teardown", function() {
  expect(2);
});

その他

テスト対象のDOM

テスト対象のDOMはqunit-fixture要素に追加します。 qunit-fixture要素はテスト終了毎にクリアされます。 テスト同士が影響を及ぼしあうのを防ぎます。

test("Appends a div", function() {
  var $fixture = $("#qunit-fixture");

  $fixture.append("<div>hello!</div>");
  equal($("div", $fixture).length, 1, "div added successfully!");
});

test("Appends a span", function() {
  var $fixture = $("#qunit-fixture");

  $fixture.append("<span>hello!</span>");
  equal($("span", $fixture).length, 1, "span added successfully!");
});

ユーザ操作のエミュレーション

jQueryのtrigger関数を使ってください。

グローバル汚染チェック

テスト実行時のURLのクエリパラメータに noglobals=true を指定するとテスト内でグローバル変数が追加・削除されていない確認することができます。 結果画面のチェックボックス「Check for Globals」にチェックを入れても一緒です。

参考