Slim3 Source Code Reading No11 Mock
Slim3 Source Code Reading #11 - ChugokuGTUG
に参加してきました!
今回も@ttyokoyama 殿と2人だったので、まったりと進みました。
まぁ、他の方が参加していても、まったり進むのですがw
T.Yokoyamaのブログ: Slim3 Source Code Reading #11
今回はtesterの中にあるMockたちです。
数はそこそこあるのですが、どれもGAEに関係しているわけではなく、ServletのRequestやResponseのMockでした。
その中に、UrlFetchHandlerというインタフェースがありました。
こいつはURLFetchServiceをテストするためのもののようで、responseを自分で作ってやれるようです。
http://code.google.com/intl/ja/appengine/docs/java/urlfetch/
公式のURLフェッチのところには、URLFetchServiceのサンプルコードはないようです。
また、こいつが本気を出すところは、非同期でURLFetchを行う時のようです。
URLFetchService#fetchAsync()を使うとGAE環境が用意しているスレッドで、サーブレットのスレッドとは別に非同期でHTTP通信を行ってくれます。
twitter4jを使う時も有効のようで、山本祐介殿のBlogでも触れられてました。
http://samuraism.jp/diary/2011/07/06/1309912740000.html
また、実際の使い方は以下のBlogに詳しく書いてあります。
今回作成したサンプルも、こちらを参考にしました。
非同期URLFetch、FetchAsync()の使い方【Google App Engine】
それでは、ソースコードです。
IndexController
public class IndexController extends Controller {/**
* Twitter検索APIをFetchする
*/
@Override
public Navigation run() throws Exception {
final String TWITER_SEARCH_URL =
"http://search.twitter.com/search.json?q=chugokugtug";
String tweetJson = fetchUrl(TWITER_SEARCH_URL, "UTF-8");List
tweetList = buildeTweetList(tweetJson); requestScope("tweetList", tweetList);
return forward("index.jsp");
}/**
* 指定したURLをFetchする
*
* @param urlStr
* @param encoding
* @return
* @throws IOException
*/
private String fetchUrl(String urlStr, String encoding) throws IOException {
URLFetchService service = URLFetchServiceFactory.getURLFetchService();
URL url = new URL(urlStr);
HTTPResponse res = service.fetch(url);
return new String(res.getContent(), encoding);
}/**
* Twitter検索APIの結果から、TweetListを作る
*
* @param searchJson
* @return
*/
private ListbuildeTweetList(String searchJson) {
ListtweetList = new ArrayList ();
try {
Gson gson = new Gson();
JsonElement element = gson.fromJson(searchJson, JsonElement.class);
JsonObject json = element.getAsJsonObject();
JsonArray array = json.getAsJsonArray("results");
for (int i = 0; i < array.size(); i++) {
Object obj = array.get(i);
if (obj instanceof JsonObject) {
tweetList.add((JsonObject) obj);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return tweetList;
}
}
テスト用に、TwitterSearchAPIを呼んでいるのですが、むしろ取得した後のJSONの処理の方が長くなってしまった・・・。
URLFetchServiceを使っているのは、fetchUrl()なので、4行しかありません。
上記のControllerをテストするコードです。
IndexControllerTest
public class IndexControllerTest extends ControllerTestCase {/**
* TwitterSearchFetchHandlerを使ったテスト
*
* @throws Exception
*/
@Test
public void testUrlFetch() throws Exception {
tester.setUrlFetchHandler(new TwitterSearchFetchHandler());tester.start("/urlfetch/");
IndexController controller = tester.getController();
ListtweetList = tester.requestScope("tweetList"); assertThat(controller, is(notNullValue()));
assertThat(tester.isRedirect(), is(false));
assertThat(tester.getDestinationPath(), is("/urlfetch/index.jsp"));
assertThat(tweetList, is(notNullValue()));JsonObject jsonObject = tweetList.get(0);
assertThat(jsonObject.get("id").toString(), is("177727132691214338"));
assertThat(jsonObject.get("from_user").toString(), is("\"sinmetal\""));
assertThat(
jsonObject.get("text").toString(),
is("\". @ttyokoyama http://t.co/POuEYdnZ 殿のコードを検証する内容でBlogを書こうとしたが、URLFetchService よりも、jsonと戦っているというw #chugokugtug\""));
}
}
テストのためのデータを、実際のデータからコピーしてきたので、僕のツイートになってますが、こんな感じです。
キモは一番最初にやってるところです。
TwitterSearchFetchHandlerがテスト用に作ったUrlFetchHandlerを実装しているクラスです。
tester.setUrlFetchHandler(new TwitterSearchFetchHandler());
中身はTwitterSearchAPIを呼んだ時と同じ内容のJSONを返しているだけです。
これでUnitTestで毎回同じ値が返ってくるようにできました。
TwitterSearchFetchHandler
public class TwitterSearchFetchHandler implements URLFetchHandler {public byte[] getContent(URLFetchRequest request) throws IOException {
String json =
"{\"completed_in\":0.084,\"max_id\":177727132691214338,\"max_id_str\":\"177727132691214338\",\"next_page\":\"?page=2&max_id=177727132691214338&q=chugokugtug&rpp=1\",\"page\":1,\"query\":\"chugokugtug\",\"refresh_url\":\"?since_id=177727132691214338&q=chugokugtug\",\"results\":[{\"created_at\":\"Thu, 08 Mar 2012 12:07:05 +0000\",\"from_user\":\"sinmetal\",\"from_user_id\":26903289,\"from_user_id_str\":\"26903289\",\"from_user_name\":\"\u771f\",\"geo\":null,\"id\":177727132691214338,\"id_str\":\"177727132691214338\",\"iso_language_code\":\"ja\",\"metadata\":{\"result_type\":\"recent\"},\"profile_image_url\":\"http://a0.twimg.com/profile_images/1042264867/1277990403846_normal.png\",\"profile_image_url_https\":\"https://si0.twimg.com/profile_images/1042264867/1277990403846_normal.png\",\"source\":\"<a href="http://www.tweetdeck.com" rel="nofollow">TweetDeck</a>\",\"text\":\". @ttyokoyama http://t.co/POuEYdnZ \u6bbf\u306e\u30b3\u30fc\u30c9\u3092\u691c\u8a3c\u3059\u308b\u5185\u5bb9\u3067Blog\u3092\u66f8\u3053\u3046\u3068\u3057\u305f\u304c\u3001URLFetchService \u3088\u308a\u3082\u3001json\u3068\u6226\u3063\u3066\u3044\u308b\u3068\u3044\u3046\uff57 #chugokugtug\",\"to_user\":null,\"to_user_id\":null,\"to_user_id_str\":null,\"to_user_name\":null}],\"results_per_page\":1,\"since_id\":0,\"since_id_str\":\"0\"}";
return json.getBytes("UTF-8");
}public int getStatusCode(URLFetchRequest request) throws IOException {
return HttpServletResponse.SC_OK;
}}
他にもErrorが発生した時なんかも作っていけば、ある程度UnitTestできそう!
前回のServletTester#addBlobKey()もマイナーな感じでしたが、今回のURLFetchHandlerもマイナーな感じのようで、ぐぐってもあんまり情報が出てきてくれないですねw
最後に余談だけど、@ttyokoyama 殿にGo言語で作られてるものって何があるんですか?と聞いたら、youtubeで使われてるよ!と以下を教えて下さった。
GitHub - vitessio/vitess: Vitess is a database clustering system for horizontal scaling of MySQL.
先日公開されたばかりで、vitessでぐぐると"もしかしてvitesse"と言われるぐらいだ!
興味がある方は、読んでみると良いのではないでしょうか。
今回の実際のソースはこちら!
IndexController
Google Code Archive - Long-term storage for Google Code Project Hosting.
IndexControllerTest
Google Code Archive - Long-term storage for Google Code Project Hosting.
TwitterSearchFetchHandler
Google Code Archive - Long-term storage for Google Code Project Hosting.