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 しかメンテされないんだし、こっちに移行しよう!」と思ったのですが・・・
あれ?
HttpTester の API 改善されてて最高だと思ったら、実行時間がアホみたいに長くなってて悲しみに暮れている。
— Kazuhiro Sera (@seratch) December 6, 2013
実行時間がめちゃくちゃ長くなってしまいました。tester.getResponses(..) のところが遅すぎで普通に 10 秒とかかかっちゃうんですよね。「さすがにそれはおかしいだろう、きっと使い方が悪いに違いない」と思って調べてみたんですが、私が試した限りではどうにもなりませんでした。上記のようなテストであれば 6.x だと一瞬で終わるのですが。
(追記)
という感じで、よくわからないなーと Twitter で日本語で愚痴っていたら Jetty の中の人から SOF で聞いてみたら?とリアクションが。
@joakime Thanks, embedded Jetty is pretty nice. I'm disappointed with ServletTester's slowdown in version 9.x though API is much improved...
— Kazuhiro Sera (@seratch) December 6, 2013
とりあえず stackoverflow にタグ付きで投稿してみました。
http://stackoverflow.com/questions/20428371/why-servlettester-in-jettty-9-x-is-so-slow
早速、こちらの方から回答がもらえました。要はそもそもが私のポカミスだったと・・
@joakime Oh, I got it. Thank you so much!
— Kazuhiro Sera (@seratch) December 6, 2013
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 ヘッダのことがすっかり頭から抜け落ちていました・・。そうか、一定時間で接続切られるまで待っていたのか・・
Jetty の件、解決しました。Jetty6 で動いてたからコードはそのままで動くんだろうと思ったけどそうではなかったと。ともあれ ServletTester はオワコンになってなくてよかったです。
— Kazuhiro Sera (@seratch) December 6, 2013
結論としては、実際は私の確認が甘かっただけで ServletTester はオワコンにはなってなかったです。まずは自分のコードを疑えってことですね。