ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

MozMill を使って Firefox 拡張機能のテストを行う方法

これまで Firefox 拡張機能のテストをどうやって書くのがいいかずっと悩んでいたのですが (悩みつつも QUnit を使ってテストを書いていました)、このあいだ MozMill というのを見つけて、使ってみたらなかなか良かったので紹介します。

MozMill とは

MozMill は Mozilla 製品 (FirefoxThunderbird などの Gecko ベースのアプリケーション) の自動テストを書くためのテスティングフレームワークです。 MDN のドキュメントには拡張機能としてインストールすることも、コマンドラインツールとしてインストールすることもできると書かれていますが、拡張機能Mozilla Add-ons から削除されてしまっているようでした。

MozMill は Firefox 自体のテストにも使われているということですが、Firefox 拡張機能のテストに使用することもできます。

MozMill を使って Firefox 拡張のテストを実行する

MozMill のインストールから Firefox 拡張機能のテストを実行するところまでの簡単な流れを説明します。

MozMill のインストール

コマンドラインツールとしての MozMill をインストールします。 DebianUbuntu では、下記コマンドでインストールできます。 (システムにインストールされている Python を使用します。) pip コマンドは Python のパッケージ管理システムです。

$ sudo apt-get install python-pip python-dev
$ sudo pip install mozmill mercurial

その他の OS の場合は MDN のドキュメントを見てください: コマンドライン版 MozMill のインストール方法

MozMill によるテストの実行

とりあえず Firefox 拡張機能は関係なしに、単に JavaScript のテストを実行させてみます。

MozMill では、「test」 で始まる名前の関数がテストの関数だとみなされるようです。 なので、

function test_XXX() {
    // ...
}

というような感じでファイル中に関数定義を書いていけばよいことになります。 とはいえ、他のテスティングフレームワークに書きかえる場合のことを考えると、テスト定義用の関数を作った方が良いでしょう。 (特に非同期処理のテストに関しては。)

var assertions = {};
Cu.import("resource://mozmill/modules/assertions.js", assertions);

// assert による値の検査を行うと、失敗時にはそれ以降の処理が行われない
var assert = new assertions.Assert();
// expect による値の検査を行うと、失敗時もそれ以降の処理が行われる
var expect = new assertions.Expect();

// --- テスト定義のための関数 ---

// XXX 同じ名前のテストが定義されたら上書きされてしまう
var global = this;
function defineTest(testName, testFunc) {
    global["test:" + testName] = testFunc;
}
function defineAsyncTest(testName, testFunc) {
    global["test:" + testName] = function () {
        var isDone = false;
        var callback = function () { return isDone };
        testFunc(function () { isDone = true });
        assert.waitFor(callback);
    };
}

// --- テストの記述 ---

defineTest("同期的なテスト", function () {
    assert.ok(true, "good");
    assert.equal("1", "1", "1 is 1");
    assert.pass("good");
});

defineAsyncTest("非同期処理のテスト", function (done) {
    var timer = {};
    Components.utils.import("resource://gre/modules/Timer.jsm", timer);
    timer.setTimeout(function () {
        expect.fail("not good");
        expect.pass("good");
        done();
    }, 1000);
});

上のようなコードを書いて (test-example.js とする)、次のコマンドを実行するとテストが走ります。

$ mozmill --binary=$HOME/local/firefox-nightly/firefox --test=test-example.js

--binary オプションは Firefox 本体のパス。 --test オプションは実行するテストファイル。

MozMill を使って Firefox 拡張機能のテストを行う

最後に Firefox 拡張機能のテストの実行方法です。

mozmill コマンドの --addon オプションにテスト対象の Firefox 拡張機能の XPI パッケージのパスを指定してやると、テスト実行前に XPI パッケージがインストールされます。 で、Firefox 拡張機能のテストコードを実行させればよいわけです。

$ mozmill --binary=$HOME/local/firefox-nightly/firefox --addon=test-target-addon.xpi --test=addon-test.js

MozMill を使ってみての感想

  • Firefox との親和性は高い。
    • Gecko ベースのアプリケーション用のテスティングフレームワークなので当然ではある。
    • mozmill コマンドを叩いたらアドオンをインストールしたうえで Firefox を起動してテスト実行してくれるという便利さ。
    • DOM 要素をラッピングしてユーザーの行動 (クリックなど) をシミュレートしてくれる機能もあるっぽい (Mozmill Element Object - Mozmill | MDN; ここら辺はまだ試してない。)
  • ドキュメントは少ない。 → MDN 上を探したら結構あるけど、リンク切れになってたりするところもあって探すのが結構大変。
    • ドキュメントはあまりないと思っていい。 ここに書いたようなテストの実行をするのにもソースコードを読んだりして調べる必要があった。
    • とはいえここに書いた程度のテストが書ければあとはまあなんとかなりそうな気もする。

そんなわけなので、Firefox 拡張機能のテストを書くなら MozMill を使うのがいいかなー、と思いました。

Firefox 周りのテストの話 (追記)

本記事公開後に saneyuki さんが Firefox 周りのテストの話についてつぶやかれていました。 Mochitest というのもあるんですね。