#概要
ラッパークラスの使いどきです。
#finalクラスをモック化したいとき
テストコードを書いていて、finalクラスをモック化したい場面があったのでラッパークラスにすることで対応しました。
元々引数にfinalクラスをとっていたメソッドだったのだけれど、それだとテストできないので引数をラッパークラスで取るようにしました。
最初からテスト駆動型で開発しておけばこういった事態にはならなかったのだろうか。。。難しい。
###元のコード
@Override
protected String doInBackground(URL... url) {
HttpURLConnection con = null;
URL urls = url[0];
try {
con = (HttpURLConnection)urls.openConnection();
con.setRequestMethod("GET");
con.connect();
int resCd = con.getResponseCode();
if (resCd != HttpURLConnection.HTTP_OK) {
throw new IOException("HTTP responseCode:" + resCd);
}
BufferedInputStream inputStream = new BufferedInputStream(con.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while (true) {
line = reader.readLine();
if (line == null) {
break;
}
mBuffer.append(line);
}
inputStream.close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
con.disconnect();
}
return mBuffer.toString();
}
###修正したコード
@Override
protected String doInBackground(URLWrapper... urlWrapper) {
HttpURLConnection con = null;
URLWrapper urls = urlWrapper[0];
try {
con = (HttpURLConnection)urls.openConnection();
con.setRequestMethod("GET");
con.connect();
int resCd = con.getResponseCode();
if (resCd != HttpURLConnection.HTTP_OK) {
throw new IOException("HTTP responseCode:" + resCd);
}
BufferedInputStream inputStream = new BufferedInputStream(con.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while (true) {
line = reader.readLine();
if (line == null) {
break;
}
mBuffer.append(line);
}
inputStream.close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
con.disconnect();
}
return mBuffer.toString();
}
doInBackgroundはAsyncTaskのメソッドです。
微妙な変化ですが、最初はjava.net.URLを引数にとっていたのですが、これだとURLはfinalクラスなのでモック化できないことに気づき、ラッパークラスに変更。
PowerMockitoを使うことも試したのですが、上手く使えなかったので、今はとりあえずこれで。
これで以下のようなテストコードがかけるようになりました。
@Test
public void test_doInBackground(){
HttpURLConnection httpURLConnection = null;
try{
String rtnXml = "aaaaaaaaaaaa";
// HttpURLConnectionのmock化
httpURLConnection = mock(HttpURLConnection.class);
when(httpURLConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
when(httpURLConnection.getInputStream()).thenReturn(new ByteArrayInputStream(rtnXml.getBytes("utf-8")));
// URLWrapperのmock化
URLWrapper urlWrapper = mock(URLWrapper.class);
when(urlWrapper.openConnection()).thenReturn(httpURLConnection);
// ConfirmAsyncListenerImplのmock化
ConfirmAsyncListenerImpl confirmAsyncListener = mock(ConfirmAsyncListenerImpl.class);
// RestaurantAsync.doInBackground()のテスト
RestaurantAsync restaurantAsync = new RestaurantAsync(confirmAsyncListener);
assertThat(restaurantAsync.doInBackground(urlWrapper), is("aaaaaaaaaaaa"));
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
httpURLConnection.disconnect();
}
}
java.net.URLはmock化しようとするとコンパイルエラーが出るので、ラッパークラスを使うと対応できることがわかりました。
ちなみにラッパークラスは以下
public class URLWrapper {
private final URL url;
public URLWrapper(URL url){
this.url = url;
}
public URLConnection openConnection(){
URLConnection urlConnection = null;
try {
urlConnection = this.url.openConnection();
}catch(IOException e){
e.printStackTrace();
}
return urlConnection;
}
}
引数をfinalクラスで取るのと、それのラッパークラスで取ることの違いやデメリットはあるのかしら。
それはまたわかったら追記します。
#まとめ
また気がついたら追記しようと思います。