2011年4月17日日曜日

JUnitでWebサービスのモックを作る

ソーシャル系のアプリやほかのWebサービスと連動するアプリの単体テストをする場合、
対向するWebサービスをどうしても利用しないとテストできないようなケースが発生する。

たとえば、Twitterアプリを作成している場合など。

Twitterのようなすでに動いているサービスが対向するwebサービスであれば、
テスト用のアカウントを作成しておいて、
そこにテスト用のTweetを入れておけばよいのではあるが、
しかしエンタープライズな場合だと、
そのような準備をできないこともある。

SIerの場合、後の工程(つまり総合試験)にそれらのテストを持って行って、
人月投入するというパターンがあるが、それはそれで、非常にまずい。

そんなわけで、手軽にWebサーバーを構築したいのだが、
tomcatにサーバーをまた立てて、モックのアプリサービスを書いて、
云々というのも非常に面倒臭い。

そこで登場するのがJettyという軽量Webサーバーです。
これはJUnitでもサーバーを立ち上げられる優れもので、テスト用にも利用出来るなかなか優れたういやつです。
ちなみに、現在はバージョン8まで登場しているそうですが、本稿で使用するのは7.3.1です。



さて、早速コードをと言いたいところですが、
今回のサンプルは先に仕様の説明から。
  • Webサーバーはlocalhostにポート3000で立てます。
  • どのようなリクエストが来ても、レスポンスコード200 : OKを返します。
  • コンテンツタイプは"application/json"を返します。
  • メッセージ部分は{"test_result" : "ok"}というjsonが設定されます。

では、早速JUnitのテストコード@BeforeClass@AfterClassからどうぞ。


public class HttpGetResourceTest {

    private static final String TEST_RESULT = "{\"test_result\" : \"ok\"}";

    private static final int PORT_NUMBER = 3000;

    private static final String LOCAL_HOST = "localhost";

    private static Server server;

    /**
     * サーバーを起動する。
     */
    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        InetSocketAddress inet = new InetSocketAddress(LOCAL_HOST, PORT_NUMBER);
        server = new Server(inet);
        
        // 後述するHandlerの実装。
        Handler handler = new RequestHandlerImpl();
        server.setHandler(handler);
        
        server.start();
    }

    /**
     * サーバーを終了する。
     */
    @AfterClass
    public static void tearDownAfterClass() throws Exception {
        server.stop();
        server.destroy();
    }



@BeforeClassではサーバーを起動して、@AfterClassはサーバーをシャットダウンします。

一応このテストクラスが実行されている間は、サーバーはローカルホスト3000番で起動します。

実際にどのような処理がなされるかは、このテストの内部クラスRequestHandlerImplに記述します。

では、RequestHandlerImplをどうぞ。


    private static class RequestHandlerImpl extends AbstractHandler {

        @Override
        public void handle(String target, Request baseRequest,
                HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException {
            
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json");
            response.setStatus(HttpServletResponse.SC_OK);
            
            Writer writer = response.getWriter();
            writer.write(TEST_RESULT);
            
            HttpConnection connection =
                HttpConnection.getCurrentConnection();
            Request req = connection.getRequest();
            req.setHandled(true);
        }
    }


ここでは本当に先程の仕様通りに
  • どのようなリクエストが来ても、レスポンスコード200 : OKを返します。
  • コンテンツタイプは"application/json"を返します。
  • メッセージ部分は{"test_result" : "ok"}というjsonが設定されます。

となっています。
あまり説明いらないですね。

では、サンプルのテストコードを書いてみましょう。
なお、このサンプルではApache HttpComponentsを利用しています。


    @Test
    public void testIsServerReady() throws Exception {
        URI uri = URIUtils.createURI("http",
                LOCAL_HOST, PORT_NUMBER,
                "/search",
                "user=mike_neck&start=10",
                null);
        HttpGet hGet = new HttpGet(uri);
        HttpClient client = new DefaultHttpClient();
        HttpResponse res = client.execute(hGet);
        
        HttpEntity entity = res.getEntity();
        
        if(entity != null){
            StringBuilder builder = null;
            InputStream input = null;
            try {
                input = entity.getContent();
                InputStreamReader reader =
                        new InputStreamReader(input, "UTF-8");
                builder = new StringBuilder();
                while(reader.ready())
                    builder.append((char)reader.read());
            }finally{
                input.close();
            }
            assertThat("testIsServerReady",
                builder.toString(),
                is(TEST_RESULT));
        }else{
            fail();
        }
    }


適当なクエリーをつけてGetで呼び出しました。
ここの部分は実際のクラスを入れておく部分ですね。



と、まあこんな感じなわけですが、
多少依存性がありますので、その情報も書いておきます。

必要なライブラリーは
commons-logging.jar
jetty-continuation.jar
jetty-http.jar
jetty-io.jar
jetty-server.jar
jetty-util.jar
servlet-api.jar

です。

皆さん、Web連携アプリのテストもしっかりとやりましょう。

2 件のコメント:

  1. テスト部分で結果を取得する部分がうまく動かず、文字列が取得できませんでした。
    Androidのテストで実行したからですかね?
    BufferedReaderをつかったら、取得できたのでお知らせしておきますね。
    ----------
    BufferedReader reader
    = new BufferedReader(new InputStreamReader(input));
    String line;
    while( ( line = reader.readLine() ) != null )
    {
    Log.d("BumpRecorderTest", line);
    builder.append(line);
    }

    返信削除
  2. こんばんは。

    サーバーのテストができるのは、ありがたいですね。

    返信削除