2013年9月30日月曜日

最強モックツール JMockit その8 例外実行

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

現在はモックツール「JMockit」の使い方をご紹介しています。

今回は例外、『Exception』の出し方についてです。

例外呼び出し


数あるテストの中で悩みがちなのが、「そのテストを実行出来ない」というパターンです。

  • ネットワークエラーのテストをしたい。⇒そのタイミングでネットワークエラーを発生出来ない。
  • 不正なファイルが来た場合に例外を発行するテストをしない。⇒それより前の処理でチェックが済んでいるので、その場所ではエラーを発行出来ない。

こういうのですね。
絶妙なタイミングでLANケーブル抜いたりとか、そうそう上手には出来ません。
そういう時こそ、モックツールの活躍のタイミングです。

JMockitを使って無理矢理例外を発生させてしまいましょう。

そのやり方が以下です。

new NonStrictExpectations() {{
      loginService.isLogin(anyString,anyString); result = new Exception();
  }};
}

ソース全体を見せるよりも、該当部分だけ記載した方が分かり易いでしょう。
resultにExceptionを設定するだけでOKです。

今回はサンプルとして「Exception」の型を使いましたが、IOExceptionとか、自前で作ったExceptionとか、例外クラスならどんなものにも対応出来ます。

特記事項としては、本体ソースがvoid型でも「result = new Exception();」の形は使えるということです。
void型なのに返り値を設定するのは変な気もしますが、そういうものだと理解して下さい。

終わりに


引き続きJMockitのご紹介をしていきます。

2013年9月25日水曜日

最強モックツール JMockit その7 複数回呼び出し

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

現在はモックツール「JMockit」の使い方をご紹介しています。

さて、今まで使ってきたJMokitのソースは、ずっと「NonStrictExpectations」を使ってきました。

第三回の時に「NonStrictExpectations」でほとんど事足りると記載しましたが、ここから先はそれ以外のケースについてご説明します。

複数回呼び出し


英単語の説明から入りますが、NonStrictExpectationsの「Strict」とは日本語で『厳密な』という意味となります。Expectationは『期待』です。
つまり、NonStrictExpectationsとは『期待値の定義を厳密にしない』という意味になります。

当然ながら、「じゃあ、厳密な場合は何を厳密にするの?」というのが当然の疑問でしょう。
チェックが厳密化するのは複数あるのですが、例として「実行順序」と「実行回数」があります。
今回は「実行順序」と「実行回数」をチェックしてテストを実行する「Expectationsクラス」のご紹介です。

「実行順序」と「実行回数」をチェックしなければならないテストケースとしては、以下を想定します。

  • Webシステムのログイン機能で、ログインに失敗した後の処理をテストする。
  • ログイン失敗が1回目、2回目の時はログイン画面に戻る。3回目以降はアカウントロック画面に遷移する。

まずは本体ソースから。

public class LoginErrorAction extends HttpServlet {
 public String doAction(HttpServletRequest req, HttpServletResponse res){

  //リクエストからエラーになったIDを取得
  String id = req.getParameter("login_id");

  LoginService service = new LoginService();

  if(service.loginErrorCount(id) <= 2){
   return "login/login.jsp";
  }

  //ログイン失敗
  return "login/loginLock.jsp";
 }
}
これについては説明はいらないでしょう。 ログイン失敗回数がDBに保存されているので、それを取得して2回以下だったらログイン画面に戻る。 3回以上だったらアカウントロック画面に遷移します。 これに対するテストケースは以下になります。
public class LoginErrorActionTest {

 /** HttpServletRequestのモック  */
 @Mocked
 private HttpServletRequest mockRequest;

 /**  HttpServletResponseのモック */
 @Mocked
 private HttpServletResponse mockResponse;

 /**  LoginServiceのモック */
 @Mocked
 private LoginService loginService;

 @Test
 public void test_3回ログインに失敗するとロック画面に遷移する() {

  LoginErrorAction action = new LoginErrorAction();
  loginService = new LoginService();

  new Expectations() {{
   loginService.loginErrorCount(anyString);
   result = 1;  //ログイン失敗1回目
   result = 2;  //ログイン失敗2回目
   result = 3;  //ログイン失敗3回目
                }};

        String jsp1 = action.doAction(mockRequest, mockResponse);
        assertThat(jsp1, is("login/login.jsp"));

        String jsp2 = action.doAction(mockRequest, mockResponse);
        assertThat(jsp2, is("login/login.jsp"));

        String jsp3 = action.doAction(mockRequest, mockResponse);
        assertThat(jsp3, is("login/loginLock.jsp"));

 }

}
ほぼほぼNonStrictExpectationsの頃と変わらないのですが、Expectationsにしたことで微妙な差異が生まれています。 まずはこれです。
loginService = new LoginService();
自分でモック化するクラスをnewしています。 NonStrictExpectationsならば勝手にインスタンスを作ってくれるのでこの行は不要でした。 Expectationsは厳密な処理を行うクラスですので、そういう自動機能は使わず、自分で明示的に指定しろということなのでしょう。 次にこちら。
new Expectations() {{
   loginService.loginErrorCount(anyString);
   result = 1;  //ログイン失敗1回目
   result = 2;  //ログイン失敗2回目
   result = 3;  //ログイン失敗3回目
   }};

呼び出し順番を指定しています。
1回目は「1」、2回目は「2」、3回目は「3」を指定しています。

JMockitの複数回指定はこのように書くわけです。
この書き方自体はNonStrictExpectationsも同じでして、NonStrictExpectationsでも同じように、1回目、2回目、3回目の指定は出来ます。

ExpectationsとNonStrictExpectationsの違いは以下です。

  • Expectations:呼び出しは3回までしか出来ない。4回呼び出すとエラーになる。
  • NonStrictExpectations:呼び出しは何回でもOK。4回目以降は3回目と同じ値になる。

なるほど、やはり厳密になっていますね。
つまり、「NonStrictExpectationsを使っていたら動いていたけど、実は見落としがあってテストケースが機能していなかった」という事態を避けることが出来るわけです。
テストの品質としてはExpectationsの方が上と言えるでしょう。

しかし、全部Expectationsを使っていては工数を消費してしまいますので、「NonStrictExpectationsで簡単に済ませて良いパターン」と「Expectationsでしっかりチェックしなければならない複雑なパターン」を切り分けてコーディングしていくことが肝心です。

終わりに


引き続きJMockitのご紹介をしていきます。

2013年9月19日木曜日

最強モックツール JMockit その6 staticクラス

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

現在はモックツール「JMockit」の使い方をご紹介しています。

今回は「staticクラス」のモック化をご紹介します。

staticクラスのモック化


staticクラスというのは、「StringUtils」とか、「FileUtils」とか、そういったタイプのクラスのことです。
staticクラスのモック化は難しいらしく、他のモックライブラリでこの機能を提供しているクラスは少ないようです。
しかし、最強モックツールであるJMockitならば簡単に出来てしまいます。

まず、本体クラスとして「StaticUtils」というサンプル用クラスを作りました。
「genesis」という文字列を返すだけの簡単なクラスです。

public class StaticUtils {

 public static String returnGenesis(){
  return "genesis";
 }

}

これをモック化すると以下のようになります。

public class StaticUtilsTest {

 /** StaticUtilsのモック  */
 @SuppressWarnings("unused")
 @Mocked
 private StaticUtils mockStaticUtils;

 @Test
 public void testReturnGenesis() {
  new NonStrictExpectations() {{
   StaticUtils.returnGenesis(); result = "ジェニシス";
  }};
        assertThat(StaticUtils.returnGenesis(), is("ジェニシス"));
 }

}


今までに紹介した書き方と殆ど同じです。強いて言えば、以下の部分でしょうか。

new NonStrictExpectations() {{
 StaticUtils.returnGenesis(); result = "ジェニシス";
}};

今までだとモック化した「mockStaticUtils」をモック対象にしていましたが、今回はstaticクラスにつき、普通に「StaticUtils.returnGenesis()」と書いています。
それ以外については、特に意識する部分はありませんね。

過去の記事も同様でしたが、ケースバイケースで書き分けねばならない難しさは余り無く、同じ要領で書き進めることが可能なのです。

終わりに


引き続き、JMockitの解析を進めます。

2013年9月17日火曜日

最強モックツール JMockit その5 privateメソッド

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

現在はモックツール「JMockit」の使い方をご紹介しています。

今回は「privateメソッド」のモック化をご紹介します。

privateメソッドのモック化

前回の記事では「内部でnewしているクラスのモック化」をご紹介しました。
内部newクラスのモック化につきましては、特に何も意識せずとも出来てしまったわけですが、
しかし、今回の「「privateメソッドのモック化」につきましては、新情報が登場します。

まずは、サンプルとして何度も出しているWebシステムのログイン機能の本体ソースを以下に記載します。

public class LoginExcuteAction extends HttpServlet {
 public String doAction(HttpServletRequest req, HttpServletResponse res){

  //リクエストからログインIDとパスワードを取得
  String id = req.getParameter("login_id");
  String pw = req.getParameter("login_pw");

  //ログイン判定
  if(isLogin(id,pw)){
   //ログイン成功
   return "menu/memu.jsp";
  }

  //ログイン失敗
  return "login/loginError.jsp";
 }

 /**
  * ログイン
  *
  * @param id
  * @param pw
  * @return
  */
 private boolean isLogin(String id,String pw){

  //DB認証
  LoginService service = new LoginService();
  boolean success = service.isLogin(id,pw);

  return success;

 }
}

前回ではログイン機能が本体メソッドの中に入っていましたが、今回はprivateメソッド「isLogin」を作成して分割しました。
これをモック化します。

つまり、今までは「HttpServletRequest,HttpServletResponse,LoginService」といった「本体が使用している他クラス」のモック化でした。
しかし今回はprivateメソッドをモック化する都合上、「LoginExcuteAction本体」をモック化しなければなりません。

このため、テストソースの方は以下になります。
public class LoginExcuteActionTest {

 /** HttpServletRequestのモック  */
 @Mocked
 private HttpServletRequest mockRequest;

 /**  HttpServletResponseのモック */
 @Mocked
 private HttpServletResponse mockResponse;

 /**  LoginExcuteActionのモック */
 @Mocked("isLogin")
 private LoginExcuteAction mockLoginExcuteAction;

 @Test
 public void ログインに成功してメニュー画面に遷移する() {

  LoginExcuteAction action = new LoginExcuteAction();

  new NonStrictExpectations() {{
   invoke(mockLoginExcuteAction,"isLogin",anyString,anyString); result = true;
        }};

        String jsp = action.doAction(mockRequest, mockResponse);

        assertThat(jsp, is("menu/memu.jsp"));

 }

 @Test
 public void ログインに失敗してログイン画面に遷移する() {

  LoginExcuteAction action = new LoginExcuteAction();

  new NonStrictExpectations() {{
   invoke(mockLoginExcuteAction,"isLogin",anyString,anyString); result = false;
        }};

        String jsp = action.doAction(mockRequest, mockResponse);

        assertThat(jsp, is("login/loginError.jsp"));

 }

}

分割してご説明しますと、まずフィールドでのモック宣言です。

/**  LoginExcuteActionのモック */
@Mocked("isLogin")
private LoginExcuteAction mockLoginExcuteAction;

今回は「テスト対象本体のモック化」ですので、このようにLoginExcuteActionをモック化する必要があります。
しかし、テスト対象本体を丸ごと全部モック化してしまってはテストの意味が無いですよね?
そういうわけで、「@Mocked("isLogin")」と書くことで、モック化したいメソッドだけをモック化して、それ以外の機能はモック化しないというスタイリッシュな手法が使用可能です。
この「モック化するクラスのうち、一部メソッドだけモック化」というのは、今回のようなprivateメソッドのモック化専用の話では無く、汎用的なシチュエーションで使用可能です。「この部分だけモック化すれば、他はそのままでいいのになぁ」という状況になったらこれを使って下さい。

次に、モック化後の振る舞いを記述する部分です。

@Test
public void ログインに成功してメニュー画面に遷移する() {

 LoginExcuteAction action = new LoginExcuteAction();

 new NonStrictExpectations() {{
  invoke(mockLoginExcuteAction,"isLogin",anyString,anyString); result = true;
        }};

 String jsp = action.doAction(mockRequest, mockResponse);

 assertThat(jsp, is("menu/memu.jsp"));

}

最初に「new LoginExcuteAction()」と、フィールドで宣言したモックのLoginExcuteActionとは別にテスト対象クラスを作っています。
しかし、実はこれ、意味がありません。
先頭でmockLoginExcuteActionを定義した為、後でnewしようが何をしようが、容赦なく暗黙的にモック化されます。
動作としては、ここでnewなんかしないで、以下全部mockLoginExcuteActionを使えば済む話です。
しかし、だからと言って「mockLoginExcuteAction」をテストメソッド本体で使用すると、「テストケースの目的がLoginExcuteActionのテストであること」がイマイチ読み取り難いものになってしまいます。
このため、私はあえてnewでクラスを作成して、テスト対象を明示するやり方を推奨します。

さて、肝心の「privateメソッドのモック化」の書き方ですが、それが以下の部分です。

new NonStrictExpectations() {{
  invoke(mockLoginExcuteAction,"isLogin",anyString,anyString); result = true;
        }};

「invoke(モックオブジェクト,メソッド名,引数,引数,……,引数)」

これでモック化完了です。
モック化するメソッド名をダイレクトに文字で書いて指定するわけです。

こうやってメソッドを普通の書き方で呼び出すのでは無く、文字列指定で呼び出すやり方を「リフレクション」と言います。
これはJavaに標準で備わっている機能でして、jmockitもそれを流用しているわけですね。

終わりに


これでprivateメソッドのモック化も出来ました。
jmockitの連載も第5回になりましたが、そろそろjmockitの使い方の勘所が分かってきたのではないでしょうか?

jmockitは特殊な機能を提供するライブラリですので最初は戸惑いますが、慣れてくると実に便利に作られていることが分かります。

次回以降も、引き続きjmockitについて解析を行います。

2013年9月9日月曜日

最強モックツール JMockit その4 内部newクラス

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

現在はモックツール「JMockit」の使い方をご紹介しています。

今回は「内部でnewしているクラス」のモック化をご紹介しましょう。

内部newクラスのモック化


前回の記事で紹介した「ログイン処理」のソースを改変して以下に記載します。

public class LoginExcuteAction extends HttpServlet {
 public String doAction(HttpServletRequest req, HttpServletResponse res){

  //リクエストからログインIDとパスワードを取得
  String id = req.getParameter("login_id");
  String pw = req.getParameter("login_pw");

  //DB認証
  LoginService service = new LoginService();
  boolean success = service.isLogin(id,pw);

  //ログイン判定
  if(success){
   //ログイン成功
   return "menu/memu.jsp";
  }

  //ログイン失敗
  return "login/loginError.jsp";
 }
}

前回モック化したのは「HttpServletRequest req」と「HttpServletResponse res」の2つです。
つまり、「引数で渡す値のモック化」でした。

こういう風に「引数で制御可能」なテストケースの作成は、比較的簡単と言えます。
しかし、今回は前回と違う部分があります。

//DB認証
LoginService service = new LoginService();
boolean success = service.isLogin(id,pw);

ここです。
本体ソースの中で「new」している部分です。

このように、「本体ソースでnewして別インスタンスを作っている」というテストケースについては、普通にJUnitテストケースを書く場合、newしているクラスの中身まで考慮して引数を渡して、テストケースを実現しなければなりません。
つまり、上記ケースの場合、「LoginExcuteAction」「LoginService」の2つのクラスが合体したテストケースになってしまうわけです。


しかし、「二つのクラスが合体させてのテスト」というのは、厳密には「結合テスト」と言ってしまって良いでしょう。
単体テストフェーズであるユニットテストで行うのは、もちろん「単体テスト」です。
よって、ユニットテストで複数のクラスを結合したテストパターンを作るのは、作るのも大変ですし、ポリシーも満たしていないので、余り理想的とは言えないのです。
(現実としては、余りその辺りのポリシーには拘らす゛、書きやすいようにやってしまうのが一般的ですが……)

そこで、今回は「LoginService」をモック化して、真の意味での「LoginExcuteActionの単体テスト」をやってみたいと思います。

そのソースはこちらです。

public class LoginExcuteActionTest {

 /** HttpServletRequestのモック  */
 @Mocked
 private HttpServletRequest mockRequest;

 /**  HttpServletResponseのモック */
 @Mocked
 private HttpServletResponse mockResponse;

 /**  LoginServiceのモック */
 @Mocked
 private LoginService mockLoginService;

 @Test
 public void ログインに成功してメニュー画面に遷移する() {

  LoginExcuteAction action = new LoginExcuteAction();

  new NonStrictExpectations() {{
   mockLoginService.isLogin(anyString, anyString); result = true;
        }};

        String jsp = action.doAction(mockRequest, mockResponse);

        assertThat(jsp, is("menu/memu.jsp"));

 }

 @Test
 public void ログインに失敗してログイン画面に遷移する() {

  LoginExcuteAction action = new LoginExcuteAction();

  new NonStrictExpectations() {{
   mockLoginService.isLogin(anyString, anyString); result = false;
        }};

        String jsp = action.doAction(mockRequest, mockResponse);

        assertThat(jsp, is("login/loginError.jsp"));

 }

}

答えを言ってしまいますと、「jmockitは、モック化対象が内部newかどうかを意識する必要は無い」です。
前回と全く同じやり方で、「@Mocked」をつけて、「NonStrictExpectations」を使って定義すれば、内部newであっても容赦なくmock化してしまいます。
実に簡単ですね。

今回は新情報として、以下の部分もご説明します。
new NonStrictExpectations() {{
 mockLoginService.isLogin(anyString, anyString); result = false;
}};

「anyString」です。

jmockitは引数のパラメータに応じて、モック化するかしないかを分岐する機能があります。

  • mockLoginService.isLogin("tacy", "password"); result = false;

こういう風に書いてあったら、それは「IDがtacy、パスワードがpasswordで引数が来た時だけモック化する。それ以外はモック化しない」という意味です。

これによってモック化を必要とするケースと必要としないケースを厳密に制御出来るのでテストケースの品質向上に繋がるわけですが、
ハッキリ言って、「何でもいいから結果がfalseなってくれればいいんだ」という風に厳密さを必要としない時の方が多いです。

今回の場合、

  • 認証に成功したらログイン成功
  • 認証に失敗したらログイン失敗

ですから、厳密な定義など不要。「true/false」だけしっかりしていれば良いのです。


そういう場合は、「NonStrictExpectations」の中で「anyString」を使って下さい。
これで「文字列ならば何でもモック化する」という意味になります。

無論、これはString型を対象にした場合です。
Integer型の場合は「anyInt」、Boolean型の場合は「anyBoolean」、汎用objectの場合は「any」など、一通り用意されておりますので、その時々に応じたものを選んで下さい。


終わりに


普通にやる場合は苦労する「内部new」クラスも、jmockitを使えば簡単に実現出来ました。

次回は、またしても普通にやっては苦労するパターン「privateメソッドのモック化」についてご紹介します。

2013年9月4日水曜日

最強モックツール JMockit その3 Webリクエスト編

株式会社ジェニシス 技術開発事業部の遠藤 太志郎(Tacy)です。

現在はモックツール「JMockit」をご紹介しています。

さて、JMockitというのは非常に高機能なライブラリでして、大きく分けて以下3つのAPIに分類できます。

  • Expectations API
  • Verifications API
  • Mockups API

しかしながら、現実のプロジェクトにおける開発風景を考えますと、


「Expectations API」の一部機能だけ分かっていれば十分


と言ってしまっても差し支え無いかと思います。

というわけで、各APIの掘り下げは以降の連載で追々ご紹介するとして、当面は「とりあえず使ってみる」をテーマとして、JMockitを使えばこういうことが出来るのだという実例をご紹介したいと思います。

最初は「Webリクエスト」です。

Webリクエストのモックテスト

ここで言うWebリクエストとは、「画面のフォームに値を入れてサブミットする」という一般的なWebシステムの通信に使用される、あのリクエストのことです。

例として「ログイン画面からIDとパスワードを入力してログインする」という機能をtomcatで実現する機能を挙げてみます。


public class LoginExcuteAction extends HttpServlet {
 public String doAction(HttpServletRequest req, HttpServletResponse res){

  //リクエストからログインIDとパスワードを取得
  String id = req.getParameter("login_id");
  String pw = req.getParameter("login_pw");

  /**
   * 実際にはここでDBにログイン認証を行う。
   * サンプルは文字列一致で済ませる。
   */
  if(id.equals("tacy") && pw.equals("password")){
                        //ログイン成功
   return "menu/memu.jsp";
  }

                //ログイン失敗
  return "login/loginError.jsp";
 }
}

このサンプルでテストすべきは以下機能です。

  • ログイン成功(ログインIDがtacy、パスワードがpassword)だったら、ログイン成功してメニュー画面へ進む。
  • ログイン失敗だったら、ログインエラー画面に進む。

至ってシンプルで平凡なWeb機能です。
画面で実際に手動でIDとパスワードを入れて動作確認するのであれば、簡単にテストできます。

しかし、これをJUnitで行うとしたら、どうでしょう?

そう、普通には出来ないんですよね。

と言うのも、「HttpServletRequest」「HttpServletResponse」はtomcatライブラリのクラスで、普通に「new」して作ることが出来ません。
(リクエストやレスポンスというのは、人間が画面に入力した項目以外にも色々なパラメータを持っていますからね。)

つまるところ、直球で「HttpServletRequest」「HttpServletResponse」を作ってJUnit実行とは出来ないのです。

そこで黒魔術登場です。
「HttpServletRequest」「HttpServletResponse」をモック化して、ダミーのリクエストでテストをやってしまいましょう!!


public class LoginExcuteActionTest {

 /** HttpServletRequestのモック  */
 @Mocked
 private HttpServletRequest mockRequest;

 /**  HttpServletResponseのモック */
 @Mocked
 private HttpServletResponse mockResponse;

 @Test
 public void ログインに成功してメニュー画面に遷移する() {

  LoginExcuteAction action = new LoginExcuteAction();

  new NonStrictExpectations() {{
   mockRequest.getParameter("login_id"); result = "tacy";
   mockRequest.getParameter("login_pw");result = "password";
                }};

                String jsp = action.doAction(mockRequest, mockResponse);

                assertThat(jsp, is("menu/memu.jsp"));

 }

 @Test
 public void ログインに失敗してログイン画面に遷移する() {

  LoginExcuteAction action = new LoginExcuteAction();

  new NonStrictExpectations() {{
   mockRequest.getParameter("login_id"); result = "tacy";
   mockRequest.getParameter("login_pw");result = "error";
                }};

                String jsp = action.doAction(mockRequest, mockResponse);

                assertThat(jsp, is("login/loginError.jsp"));

 }

}

まず、フィールドにアサーション「@Mocked」を宣言して「HttpServletRequest」「HttpServletResponse」のモックを作ります。
そして、各メソッドの「new NonStrictExpectations」の中で、モックとしての挙動を定義します。
最後にモックを引数として普通にメソッドを実行すれば、テスト成功です。

簡単ですね!!


終わりに


今回紹介した「@Mocked」+「new NonStrictExpectations」の組み合わせ、これがjmockitで一番簡単なやり方です。

もちろん違う書き方もあり、例えば「モックが呼ばれた回数も確認したい」「モックを呼ぶ順番も制御したい」など細かな要望にも対応可能ですが、
現実として、大概はこの「@Mocked」+「new NonStrictExpectations」で済んでしまうかと思います。

次回もjmockit活用法のサンプルをご紹介します。