読者です 読者をやめる 読者になる 読者になる

$shibayu36->blog;

株式会社はてなでエンジニアをしています。プログラミングや読書のことなどについて書いています。

TypeScriptのテスト環境でDOMも含めたユニットテストを行う

tech

TypeScriptでのフロントエンド開発環境作成総まとめ - $shibayu36->blog;Karma, Mocha, Chaiを使ってTypeScriptでのテスト環境を構築する - $shibayu36->blog;の続きです。

JavaScriptで実装していると、できる限りDOM操作とロジックを分割しても、DOMを触るコードがある程度存在してしまいます。個人的にはその時にDOMも含めた簡単なユニットテストを書いておけると、開発もしやすくなるし、少し安心感も出ます。

karmaを利用してJSのテストを実行しているのであれば、karma-html2js-preprocessorを利用すればDOM操作やクリックイベントに伴う変化とかそういうのもテストできるのですが、TypeScriptを使っていて少しだけハマったのでメモとして残しておきます。

karma-html2js-preprocessorの導入

この辺を参照すれば結構簡単に導入できます。前回TypeScriptでテストする環境を導入したので、その続きから導入します。

まずkarma-html2js-preprocessorをインストール。

$ npm install karma-html2js-preprocessor --save-dev

karma.conf.jsのfilesとpreprocessorsに追加。今回はテスト用のHTMLファイルはsrc/ts/test/以下に配置するようにしました。

karma.conf.js

    files: [
        'src/ts/test/**/*.ts',
        'src/ts/test/**/*.html'
    ],
    preprocessors: {
        'src/ts/**/*.ts': ['browserify'],
        'src/ts/test/**/*.html': ['html2js']
    },

簡単なテストを書く

今回は適当なulにliを挿入するという雑なDOM操作実装を作って、そのテストを書いてみます。

実装(src/ts/appendList.ts)

export default function ($container: JQuery): void {
    // 渡された$containerにliを挿入するだけ
    $container.append('<li></li>');
}

ユニットテスト用に簡単なHTMLを用意(src/ts/test/appendList.html)。

<ul class="js-list">
</ul>

テスト(src/ts/test/appendList.ts)

/// <reference path="../../../typings/browser.d.ts" />

"use strict";

import * as $ from "jquery";
import { assert } from "chai";
import appendList from "../appendList";

describe("appendList default function", () => {
    it("append list to container", () => {
        document.body.innerHTML = __html__['src/ts/test/appendList.html'];

        let $container = $(".js-list");

        assert.equal($container.find('li').length, 0, "最初は0件");

        appendList($container);
        assert.equal($container.find('li').length, 1, "追加したので1件増える");

        appendList($container);
        assert.equal($container.find('li').length, 2, "追加したので2件に増える");
    });
});

これで$(npm bin)/karma startでテストを動かしてみると、エラーが起こります。

TypeScript error: src/ts/test/appendList.ts(11,35): Error TS2304: Cannot find name '__html__'.

これはTypeScriptを使っているので、使う変数に型定義をつける必要があるのですが、karma-js2html-preprocessorで導入される__html__変数には型定義がないので、エラーが発生しているようでした。これだとテストが出来なくて困ります。また、これ用の定義ファイルもDefinitely Typedでは見つかりませんでした。

型定義をする

今回の問題であれば、自分で適当にこの__html__変数について型定義してあげれば良さそうです。__html__変数はstringをkeyとしてstringを返す連想配列の構造をしているので、そのように定義してあげます。テスト用にsrc/ts/test/以下にtest.d.tsという型定義ファイルを用意して、 http://www.atmarkit.co.jp/ait/articles/1501/29/news117_4.html を見ながら適当に定義します。

src/ts/test/test.d.ts

// テストファイル中に型定義へのreferenceをいくつも書きたくないので、
// browser.d.tsも読み込んでおく
/// <reference path="../../../typings/browser.d.ts" />

// karma-html2js-preprocessorで
// __html__変数を使うので定義を作っておく
declare var __html__: { [path: string]: string };

あとは先程のsrc/ts/test/appendList.tsで、この型定義ファイルにreferenceするだけです。

/// <reference path="./test.d.ts" />

"use strict";

import * as $ from "jquery";
import { assert } from "chai";
import appendList from "../appendList";

describe("appendList default function", () => {
    ...
});


* まとめ
TypeScriptのテスト環境で、DOM周りのテストを行う時に少しハマったのでメモとして書いておきました。もしもっと良い方法があったら教えてほしいです。

また今回の変更点は https://github.com/shibayu36/typescript-project-sample/pull/2 においてあります。

** 関連
- [http://blog.shibayu36.org/entry/2016/04/26/110000:title]
- [http://blog.shibayu36.org/entry/2016/04/12/180000:title]
- [https://github.com/karma-runner/karma-html2js-preprocessor]
- http://qiita.com/hosomichi/items/311e3eac4f55183b62c2:title