Scenic3のAppUrlsに関するテスト

sue445さんのエントリーにもあるように、Scenic3の弱点の1つにAppUrlsに各ページクラスを個別に登録しなくてはならないという事があります。これは、ページクラスからAPTを使ってControllerクラス群を生成し、さらにそれらのControllerをディスパッチするMatcherを生成する仕組みでScenic3が設計されている事に起因します。APTの制約として、1つのクラスから抽出できるアノテーションから複数のクラスは生成できるのですが、幾つかのクラスに分散したアノテーションをまとめる事はできないのです*1。したがって、次のような欠点があるのです。

Controllerクラスが増えすぎずStrutsライクにコードを書けるため結構使いやすいのですが、Pageクラスを追加する度にAppUrlsにも対応するMatcherクラスを追加しないといけないのが難点です。

解決方法の1つとしては、AppUrlsを動的に生成するという事が考えられます。つまり、クラスをクロールするわけです。しかし、この解決方法ではSpin-upに影響が大きいという問題があるため、GAEでは採用できません*2
でも、自分で使っていても、Matcherを追加し忘れるんです・・・。

これについては何か解決策ないかなーとは思いつつ、ユニットテストをすれば気付くからいいや*3と思う所もあり、優先度は低く考えていました。ですが、ユニットテストを書いて走らせてみると「あれ?なんで通らないんだろ・・・」という事があるわけです。これはちょっと良くないです。

というわけで、sue445さんのエントリーにヒントを得て、MatcherにPageクラスが登録されているかを検証する事に対応しました。ただし、バージョン0.5.0(3月末までにリリース予定)からです。使い方としては、こんな感じ。

public class SimplePageTest extends PageTestCase {

    public SimplePageTest() {
        super(AppUrls.class, SimplePage.class);
    }
   // test
}

これまでもPageTestCaseというのはありましたが、コンストラクタを改良し、どのAppUrlsを使い、どのPageクラスのテストであるかを引数で渡すようにしています。コンストラクタでは、AppUrlsにPageクラスが登録されているかを検証し、登録されていない場合は、コンストラクタの時点でテストが失敗するようにしました。

junit.framework.AssertionFailedError: scenic3sample.controller.AppUrls don't contains scenic3sample.page.SimplePage
at junit.framework.Assert.fail(Assert.java:47)
at junit.framework.Assert.assertTrue(Assert.java:20)
at scenic3.tester.PageTestCase.(PageTestCase.java:34)
at scenic3sample.page.SimplePageTest.(SimplePageTest.java:12)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:202)
//略

欠点としては、ユニットテストを書かないと結局は検証できないと言うことですが、ほとんどの人は書くと信じています。

尚、検証メソッド自体は、staticメソッドとしてPageTestCaseに定義されていますので、次のように直接テストしても構いません。

public class AppUrlsTest {
    @Test
    public void contains_SimplePage() throws Exception {
        PageTestCase.assertAppUrls(AppUrls.class, SimplePage.class);
    }
}

ただし、テストに追加するのを忘れるのでオススメしません。

*1:個々のクラスが独立してAPTを発火するため

*2:アイディア元のt2-frameworkではクロールするアプローチがとられている

*3:GoogleAppEngineを使う人であればユニットテストはしますよね!