QUnitの概要

## QUnitとは
JavaScript用のTestingFrameworkです。

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

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

他のツールと連携すると

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

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

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

```html
<!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はこのような内容です。

```js:test.js
test("hello test", function() {
  ok(1 == "1", "passed!");
});
```

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

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

```javascript
test("hello test", function() {
	ok(1 == "1", "passed!");
});
```
第一引数がテストケース名で第二引数に内容を書きます。

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

1. ok
1. equal
1. throws

##### OK
真であるか判定します。

```js
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
二つの値が同じであるか判定します。

```javascript
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関数もあります。

```javascript
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もあります。

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

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

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

- notEqual
- notStrictEqual
- notDeepEqual

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

```javascript
test("throws", function() {
	throws(
		function() {
			throw "error"
		},
		"throws with just a message, no expected"
	);
});
```
#### Callbackのテスト
Callback関数が呼ばれるかどうかをテストする方法があります。

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

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

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

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

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

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

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

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

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

```javascript
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関数を使うとテストケースをグループ化できます。

```javascript
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関数を指定してできます。これらの関数は各テストの実行前後に呼び出されます。

```javascript
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要素はテスト終了毎にクリアされます。
テスト同士が影響を及ぼしあうのを防ぎます。

```javascript
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」にチェックを入れても一緒です。

### 参考
- [本家](http://qunitjs.com/)
- [Cookbook](http://qunitjs.com/cookbook/)
- [QUnit API Documentation](http://api.qunitjs.com/)