seratch's weblog in Japanese

About Scala, Java and Ruby programming in Japaense. If you need English information, go to http://blog.seratch.net/

Jetty の ServletTester の挙動がおかしいと思ったら自分がおかしかったでござる

Jetty に ServletTester というのがあってですね、生 Servlet のテストをしたいときなんかに結構重宝してたりしたんですよ。

<dependency>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jetty-servlet-tester</artifactId>
    <version>6.1.26</version>
    <scope>test</scope>
</dependency>

こんな感じにテストが書けます。

package example;

import org.junit.Test;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.mortbay.jetty.testing.HttpTester;
import org.mortbay.jetty.testing.ServletTester;

public class SampleServletTest {

    @Test
    public void testDoGet() throws Exception {
        ServletTester tester = new ServletTester();
        tester.addServlet(SampleServlet.class, "/index");
        tester.start();

        HttpTester request = new HttpTester();
        request.setMethod("GET");
        request.setHeader("Host", "tester"); // should be "tester"
        request.setURI("/index");
        request.setVersion("HTTP/1.1");
        request.setContent("");

        String responses = tester.getResponses(request.generate());
        HttpTester response = new HttpTester();
        response.parse(responses);

        assertThat(response.getStatus(), is(equalTo(200)));
    }

}

ただ、この API、パッと見ておわかりいただけると思うんですが、すごく・・変ですよね・・・。なんというか無理矢理感がすごいというか。

ただ、現実問題として生 Servlet と戦うときにこのテストツールは強力な武器になってくれるので、そこはまあ目をつぶるわけですよ。

org.eclipse になって

で、Jetty が 8,9 で org.eclipse に移管されて、パッケージも org.mortbay.jetty から org.eclipse.jetty になった段階で書き換えられたようです。今回は Servlet 3.1 ではなく Servlet 3.0 をやっていたので 9.0.x を示しています。

<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-servlet</artifactId>
    <version>9.0.7.v20131107</version>
    <scope>test</scope>
</dependency>

で、こんな感じに書けるようになったと。

package example;

import org.junit.Test;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.servlet.ServletTester;

public class SampleServletTest {

    @Test
    public void testDoGet() throws Exception {
        ServletTester tester = new ServletTester();
        tester.addServlet(SampleServlet.class, "/index");
        tester.start();

        HttpTester.Request request = HttpTester.newRequest();
        request.setMethod("GET");
        request.setHeader("Host", "tester"); // should be "tester"
        request.setURI("/index");
        request.setVersion("HTTP/1.1");
        request.setContent("");

        HttpTester.Response response = HttpTester.parseResponse(tester.getResponses(request.generate()));

        assertThat(response.getStatus(), is(equalTo(200)));
    }

}

まあ、これでも冗長さはあるわけですけど Request/Response の無理矢理感はなくなって、だいぶマシになりましたね。パッケージやメソッド名なんかも妥当な感じに改善されているんです。「これはいいな、今後は org.eclipse しかメンテされないんだし、こっちに移行しよう!」と思ったのですが・・・

あれ?

実行時間がめちゃくちゃ長くなってしまいました。tester.getResponses(..) のところが遅すぎで普通に 10 秒とかかかっちゃうんですよね。「さすがにそれはおかしいだろう、きっと使い方が悪いに違いない」と思って調べてみたんですが、私が試した限りではどうにもなりませんでした。上記のようなテストであれば 6.x だと一瞬で終わるのですが。

(追記)

という感じで、よくわからないなーと Twitter で日本語で愚痴っていたら Jetty の中の人から SOF で聞いてみたら?とリアクションが。

とりあえず stackoverflow にタグ付きで投稿してみました。

http://stackoverflow.com/questions/20428371/why-servlettester-in-jettty-9-x-is-so-slow

早速、こちらの方から回答がもらえました。要はそもそもが私のポカミスだったと・・

Jetty 9 のサーバは HTTP/1.1 なんだけど

Jetty 9's HttpTester also defaults to HTTP/1.0.

とのことで(HttpTester はリクエストをつくってるやつですね)私のサンプルは HTTP/1.1 指定してるんだから

request.setHeader("Connection", "close");

つけるか HTTP/1.0 でリクエストしないとダメということでした。Connection ヘッダのことがすっかり頭から抜け落ちていました・・。そうか、一定時間で接続切られるまで待っていたのか・・

結論としては、実際は私の確認が甘かっただけで ServletTester はオワコンにはなってなかったです。まずは自分のコードを疑えってことですね。