日々常々

ふつうのプログラマがあたりまえにしたいこと。

JAX-RSのユニットテストまわり

JAX-RSのClient APIを確認した流れで、ユニットテストまわりも眺めてみる。

  • Jersey - 2.17
  • RESTEasy - 3.0.9.Final

各実装のテストサポート

軽くコード書いてみたりしつつ思ったことをば。

Jerseyさん

Jerseyさんのテスティングフレームワークを使うには、jersey-test-framework-core と、テストで使うコンテナのproviderが要ります。providerはいくつか用意されているので、好きなのを依存に追加しましょう。

    testCompile "org.glassfish.jersey.test-framework:jersey-test-framework-core:${jerseyVersion}"
    // grizzly2, inmemory, jdk-http, simple, jetty...
    testCompile "org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:${jerseyVersion}"

providerは自分で作ることもできるし、複数を使い分けることもできます。使い分ける需要がどれほどあるかはわからないけど。 テストでカバーできる範囲をproviderの選択で切り替えれる感じかな。何を選択するかはどんなテストをしたいかとかにもよるけれど、実行速度の差は結構あるので一つの判断材料になるかもしれない。(jdk-httpだと20ケースで40秒程度かかる。inmemoryだと20ケース2秒程度で、500ケースだと17秒。grizzlyもたいがい早い。数増えると結構変わりますね。)

JerseyTestを使用したテストコードはこんな感じ。

public class HogeTest extends JerseyTest {

    @Test
    public void test() throws Exception {
        String response = target("hoge")
                .request()
                .get(String.class);
        assertThat(response, containsString("HOGE"));
    }

    @Override
    protected Application configure() {
        return new ResourceConfig(Hoge.class);
    }
}

ポイントは前に紹介したJAX-RSのClientAPIがそのまま使用されている点。なのでコード内に登場するテストに特化した知識はこれくらいです。

  • テストクラスでJerseyTest を継承すること。
  • configureメソッドをオーバーライドすること。

ざっくり書いたコード

JerseyTestJUnitRuleにしてもいいとは思いますが、Jerseyのテストに特化するなら継承枠を使っていいと思います。Ruleあったらそっち使うけどね。

RESTEasy

こちらのアプローチはMockを使う形です。そしてこのMockさんたちはresteasy-jaxrsに直接入っているため、別に依存ライブラリを追加する必要はありません。コンテナ使うのはArquillianをーって住み分けなんだろうか。

    @Test
    public void test() throws Exception {
        Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
        dispatcher.getRegistry()
                .addResourceFactory(new POJOResourceFactory(Hoge.class));

        MockHttpResponse response = new MockHttpResponse();
        dispatcher.invoke(
                MockHttpRequest.get("hoge"),
                response);

        assertThat(response.getContentAsString(),
                containsString("HOGE"));
    }

継承などを使用しない分、Dispatcherまわりはゴテゴテする感じだけれど、プロダクトで採用するならこの辺はRuleにするだろうから問題ない。DispatcherRequestResponseを渡してResponseを検証する形は独特だけれど、それほど違和感もないと思います。 これを記憶を頼りに書くのは少々厳しく感じますので、使うときはドキュメントを眺めるとしましょうか。

こちらのすぐに挙げられる利点は500ケースでも5秒程度と言う結構な早さ。

ざっくり書いたコード

まとめのような雑感

JAX-RSPOJOで書けるのが強みなので、JAX-RSのクラスに書かれたロジックをテストしたいなら素のJUnitのテストとして書くのがいいです。それなら500ケースでも0.1秒とかそんなのですしね。

ここで挙げた子たちは、JAX-RSならではの機能のテストに限定するべきでしょう。たとえば@Pathに書いたのが期待通りになっているかとか、ProviderやFilterが期待通り動いてるかとか。 なんでもかんでもフル環境でやってるとフィードバックが遅くなるので、なるべく小さく確認できる方法を知っておくと色々捗ります。それぞれの道具を使うことで、何を検証できるか(そして何ができないか)を把握しておくと良いです。

APIデザインの極意に「テストをサポートしないのは開発者をがっかりさせる簡単な方法だ(やや曲解)」って書いてたりするけれど、だいたいのよく知られたフレームワークはテストをサポートしています。ここで挙げた子たちともうまくやっていきたいなと思いました。まる。

APIデザインの極意 Java/NetBeansアーキテクト探究ノート

APIデザインの極意 Java/NetBeansアーキテクト探究ノート

メモ: gradleとか使わずにJerseyTestを使おうとしたならば

jar4個突っ込めば動きそうな気配はある。 →Gistに投げ込んだのを参照

でもたまに書いてるけど、手作業で依存をごにょごにょする時代は終わってますやね。