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

play framework 小ネタ2 -依存モジュールのユニットテストをスキップさせる-

ちょっと間が空きました。それにしてもうんざりする暑さが続きますね。


さて、Playにはプログラムの固まりをモジュール化することができ、他のプロジェクトでそのモジュールに依存することで既存のプログラムを使いまわすことができます。
モジュールには既にPlayのリポジトリに用意されているサードパーティーなモジュール(secureやcoberturaとか)が存在していて、これらを使われている方も多いかと思いますが今回は独自モジュールを使った場合のユニットテスト(UT)に関するお話。


例えば、以下の様な構成のプロジェクトを作ったとします。

          hoge-common(module)
            ↑             ↑
      hoge-web         hoge-admin
    (application)     (application)

hoge-web, hoge-adminはPlayのWebアプリケーション。それらがhoge-commonという独自モジュールに依存しています。commonにはModelだったり共通的に使えるContoller等々のモジュールがあるとし、これらをwebとadminで使いまわせる構成です。

実際のプログラムはこれで問題なく共有できるってことで、今度はやはりUTもちゃんと書かないとねという話になります。やはりweb/admin/commonを問わず、UTは書きたいですよね。PlayのUTはそれぞれのプロジェクトのtestディレクトリ以下に、UnitTestやFunctionalTestを継承して作成します。


作成したUTをまとめて走らせるには

$ play test

$ play auto-test

で実行できます。ちなみにテストはtestrunnnerというモジュールにより実行されます。


しかし、ここで罠が1つ。
commonに依存したweb/adminでUTを実行させると、依存したモジュールであるcommonのテストも実行されてしまいます。正直、依存される側のテストが少なければあまり気にならないんですが、テストが積み上がってくるとcommonで行われたUTがweb/adminで実行されるのは時間の無駄です。UTをJenkinsで自動化しようがしまいが同じですね。


というわけで、どうにかしたいと思ったためtestrunnerのソースを見てみました。UT実行の対象となるプログラムを抽出している箇所が以下のようなコード。

public class TestRunnerPlugin extends PlayPlugin {

    @Override
    public void onLoad() {
        VirtualFile appRoot = VirtualFile.open(Play.applicationPath);
        Play.javaPath.add(appRoot.child("test"));
        for (VirtualFile module : Play.modules.values()) {
            File modulePath = module.getRealFile();
            if (!modulePath.getAbsolutePath().startsWith(Play.frameworkPath.getAbsolutePath()) && !Play.javaPath.contains(module.child("test"))) {
                Play.javaPath.add(module.child("test"));
            }
        }
    }

    ...
}

ここのforループでモジュールのテストがUTの実行対象として加えられています。つまり、ここをコメントアウトしてやれば良いと。

public class TestRunnerPlugin extends PlayPlugin {

    @Override
    public void onLoad() {
        VirtualFile appRoot = VirtualFile.open(Play.applicationPath);
        Play.javaPath.add(appRoot.child("test"));
        //for (VirtualFile module : Play.modules.values()) {
        //    File modulePath = module.getRealFile();
        //    if (!modulePath.getAbsolutePath().startsWith(Play.frameworkPath.getAbsolutePath()) && !Play.javaPath.contains(module.child("test"))) {
        //        Play.javaPath.add(module.child("test"));
        //    }
        //}
    }

    ...
}

修正はこれだけ。実際のUTはtestrunnerのJarを参照して行われているので、${PLAY_HOME}/modules/testrunner/build.xmlをantで実行すればJarが更新されるのでOK。


以上です。もしかしたらもっとスマートな方法があるのかもしれないですが、今回はこんなところで。