こんにちは。itouです。
今回は、外部サーバへHTTPリクエストする機能をJUnitでテストをする際
実際に外部サーバを建てることなくテストできる方法を記載いたします。
検証環境
- Eclipse 4.4.1
- Java 1.7
- JUnit 4.12
- JMockit 1.17
- httpclient 4.4.1
Eclipse動作確認手順
Eclipseプロジェクトを添付しておりますので
動作を確認しながらご覧いただければ幸いです。
opentone_labs_201506.zip
- 「ファイル」-「インポート」-「既存プロジェクトをワークスペースへ」を選択
- 「アーカイブ・ファイルの選択」でopentone_labs_201506.zipを選択しインポート
- プロジェクトを右クリックし、「実行」-「JUnit テスト」
- JUnitが全て緑(成功)していることを確認
テスト対象機能説明
今回は「引数のURLに対してGETでHTTPリクエストを行い、HTTPステータスを返却する」という機能をテストします。
・HttpServiceImpl.java(テスト対象機能)
[code]
@Service
public class HttpServiceImpl implements HttpService {
@Override
public int requestUrl(String url) throws IOException {
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
int statusCode;
try {
// CloseableHttpClientを生成
httpClient = HttpClients.createDefault();
// GETでHTTPリクエストを作成
HttpGet request = new HttpGet(url);
// HTTPリクエストを行い、HTTPレスポンスを取得
response = httpClient.execute(request);
// HTTPレスポンスよりHTTPステータスを取得
statusCode = response.getStatusLine().getStatusCode();
} finally {
if (httpClient != null) {
// CloseableHttpClientをclose
httpClient.close();
}
if (response != null) {
// CloseableHttpResponseをclose
response.close();
}
}
// HTTPステータスを返却
return statusCode;
}
}
[/code]外部サーバがある状態でのJUnit
比較の為、まずは外部サーバがある状態でJUnitを実施してみます。
外部サーバにはexample.comを指定します。
ブラウザで https://example.com/ と指定すれば表示されるとおり、実存するサーバです。
・UnusedMockedHttpServiceTest.java
[code]
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/application-context.xml" })
public class UnusedMockedHttpServiceTest {
@Autowired
private HttpService httpService;
/**
* HTTPステータス=200 OK
*
* @throws Exception
* Exception
*/
@Test
public void example200() throws Exception {
// http://example.com/ にHTTPリクエスト
int actual = httpService.requestUrl("http://example.com/");
// HTTPステータスが200 OKであること
assertThat(actual, is(HttpStatus.OK.value()));
}
/**
* HTTPステータス= 404 Not Found
*
* @throws Exception
* Exception
*/
@Test
public void example404() throws Exception {
// http://example.com/not_found にHTTPリクエスト
int actual = httpService.requestUrl("http://example.com/not_found");
// HTTPステータスが404 Not Foundであること
assertThat(actual, is(HttpStatus.NOT_FOUND.value()));
}
}
[/code]http://example.com/ は存在するURLなので
HTTPステータス 200:OKが返却されます
http://example.com/not_found は存在しないURLなので
HTTPステータス 404 Not Foundが返却されます
外部サーバがない状態でのJUnit
いよいよ、本題の
外部サーバを使用せずにテストをした場合の方法を記載いたします。
外部サーバを指定してリクエストを発行する代わりに、
どのような記述になっているか着目してみてください。
・UsedMockedHttpServiceTest.java
[code]
/**
* HTTPステータス=500 Internal Server Error
*
* @throws Exception
* Exception
*/
@Test
public void example500() throws Exception {
// HTTPステータス500 Internal Server Errorが返却されるようモックに設定
mockHttpClientSet(HttpStatus.INTERNAL_SERVER_ERROR.value());
// モックを使用するので、実際には ★dummy★ にリクエストされない
int actual = httpService.requestUrl("★dummy★");
// HTTPステータスが500 Internal Server Errorであること
assertThat(actual, is(HttpStatus.INTERNAL_SERVER_ERROR.value()));
}
[/code]requestUrlの引数に”★dummy★”と指定して、外部サーバを指定せずにリクエストを発行しましたが
HTTPステータス「500 Internal Server Error」が返却されてテストOKとなっています。
HTTPリクエスト前に実施している「mockHttpClientSet」にご注目ください。
後述する「モック」と呼ばれる仕組みを利用すると、返却するHTTPステータスをあらかじめ指定することができます。
これによって外部サーバに実際にアクセスしなくても、テストしたい振る舞いに合わせた
HTTPリクエストの結果を指定することができます。
モックの仕組み
今回は「JMockit」というモックフレームワークを使用しています。
「JMockit」の詳細は http://jmockit.org/ をご参考ください。
今回は、レスポンス値を指定するモックの機能に絞ってご説明いたします。
モックを定義しているスーパークラスのAbstractMockedTest.javaを確認します
CloseableHttpClientをモック化し
CloseableHttpClientのexecuteを使用する場合は
mockHttpClientSetで指定のHTTPステータスにより生成した、HttpResponseImplDummyが返却されるようにしています。
・AbstractMockedTest.java
[code]
public abstract class AbstractMockedTest {
@Mocked
private CloseableHttpClient mockHttpClient;
/**
* CloseableHttpClient#execute のモック設定
*
* @param httpStatus
* HTTPステータス
* @throws IOException
* IOException
*/
protected void mockHttpClientSet(final int httpStatus) throws IOException {
new NonStrictExpectations() {
{
// モックの対象メソッドを指定
mockHttpClient.execute((HttpUriRequest) any);
// CloseableHttpClient#executeの戻り値を生成
result = new HttpResponseImplDummy(httpStatus);
}
};
}
}
[/code]・HttpResponseImplDummy.java
[code]
public class HttpResponseImplDummy implements CloseableHttpResponse {
/**
* ステータスライン
*/
private StatusLine statusLine;
/**
* HTTPレスポンスのうち、ステータスライン(HTTPステータス)を定義
*
* @param httpStatus
* HTTPステータス
*/
public HttpResponseImplDummy(int httpStatus) {
setStatusLine(new StatusLineImpl(httpStatus));
}
/**
* ステータスラインを実装
*
* @author opentone
*
*/
public class StatusLineImpl implements StatusLine {
private final ProtocolVersion protocolVersion;
private final int statusCode;
private final String reasonPhrase;
private StatusLineImpl(int statusCode) {
this.protocolVersion = new ProtocolVersion("HTTP", 1, 1);
this.statusCode = statusCode;
this.reasonPhrase = "OK";
}
@Override
public ProtocolVersion getProtocolVersion() {
return protocolVersion;
}
@Override
public int getStatusCode() {
return statusCode;
}
@Override
public String getReasonPhrase() {
return reasonPhrase;
}
}
@Override
public StatusLine getStatusLine() {
return this.statusLine;
}
@Override
public void setStatusLine(StatusLine arg0) {
this.statusLine = arg0;
}
:
:
以下略
:
:
[/code]まとめ
モックを利用することで外部サーバを建てずにHTTPリクエストのテストをする方法を紹介しました。
また今回はご紹介しませんでしたが、
モックの呼ばれた回数や、モックに渡されたパラメータの検証も可能です。
詳しくは、 http://jmockit.org/ をご参考ください。
HTTPリクエストのテストにおけるモック活用のご紹介でした。

