ラベル 最強モックツールJMockit編 の投稿を表示しています。 すべての投稿を表示
ラベル 最強モックツールJMockit編 の投稿を表示しています。 すべての投稿を表示

2014年5月8日木曜日

【追加】最強モックツール JMockit その13 DI(Spring)対応

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

只今、私の現場ではこのブログを参考資料としてJUnit開発を進行中です。

その現場にて必要になった新情報を補強連載しております。

DI

今回の記事で重要になるキーワード、それは「DI」です。
依存性の注入(Dependency injection)という意味でして、Javaのクラス宣言を普通に「new ○○」とやるのではなく、外部XML等に宣言して
クラス間の結合を疎にするというソフトウェアパターンです。

このDIの機能を提供してくれるフレームワークをDIコンテナと言います。

DIコンテナの代表格と言えば、やっぱり「Spring」ではないでしょうか?

老舗にして大御所。
Javaを使っている人は、いつかどこかでお世話になるフレームワークかと思います。

DIの利便性

DIを使うことの利点、それは「クラス間が疎結合になる」ことです。

と言いましても、ピンと来ない人も多いかもしれませんね。


「クラス間が疎結合になったから何だって言うんだ?」


全く普通の感想かと思います。

DIコンテナを使うと、クラスを生成する際に普通に「new ○○」とは書けなくなって、
XMLファイルに色々宣言したりと手間が掛かるようになります。

私もいくつかの現場でSpringを使った事がありますが、振り返って考えてみると、
「クラス間が疎結合になり、ただ面倒になっただけ」という失敗ソースが多かったですね。

Springは結構難しいフレームワークなので、深く理解してからクラス設計しないと大失敗に陥ってしまいます。

DIコンテナについて記述していくと一回の記事で終わりませんので手短に行かせて貰いますが、
DIコンテナの利便性の一つ、それは「スタブが使い易い」ということです。

DIによるスタプ

まずはDIを使った簡単なサンプルを以下に記述します。

public class Sample01 {
 
 /** DIコンテナで自動セットするDAO */
 @Autowired
 private SampleDao sampleDao;
 
 public void test() {
  
  //DB検索実行
  sampleDao.findAll();
  
 }

}

このソース、「new SampleDao()」なんてやってる箇所が無いですよね?
SampleDaoはDIによってクラス生成時に自動セットされますので、自分でnewする必要は無いのです。
「@Autowired」のアサーションが、自動セットするフィールドであることを示すマークです。

これは分かっている人にとってはソースがスッキリして良いのですが、分からない人にとっては混乱するだけという、ちょっとハードルのある機能です。

さて、この「SampleDao」は、実はインターフェースでして、実クラスではありません。
実クラスが何なのかはソース中ではなく外部クラスに定義されています。


DIの特徴:ソース中に出てくるのはインターフェースだけ。実クラスは出てこない。


では、実クラスはどうやってセットされるのか?
それを外部のXMLファイルに記述するわけです。

<bean id="target" class="jp.co.net.genesis.sample.Sample01Impl" / >

こんな感じです。

インターフェースの中身は外部定義ファイルに定義されます。
これがDIコンテナの最大の特徴。


  • 本番時は本番用の外部定義ファイルを読み込む。⇒本物のクラスを定義
  • テスト時はテスト用の外部定義ファイルを読み込む。⇒テスト用スタブを定義


こうすることで、ソースの中身に手を加えることなく本番モードとテストモードを切り替えることが出来るわけです。

この機能は大規模開発で役に立つことが多いですね。
複数のチームで一つのシステムを作るような大規模開発の場合、一つのチームの進捗遅れや手違いで全チームが影響を受けることもしばしば。

しかし、DIコンテナを使うことで、
「本番用ソースは別チームが開発中」、「こっちは先にスタブでテストを済ませておく」みたいな戦術が使えるようになり、
チーム毎やクラス毎の独立性が確保されるようになるわけです。

DIのモック化

しかしですね、上記の場合、「わざわざ本番用とスタブの2コのクラスを作らなければならない」という手間もあるわけですよ。


スタブを作るのが面倒臭い!!


結構あるんですよ。
ちょっとの機能なのに一々スタブを作るなんて面倒でやってられない、とか。

だったら最初からDIコンテナなんて使うな、と言いたい所なのですが、現場の事情によることもありますので、
JMockitにて柔軟に対応していきましょう。

public class Sample01Test extends ContextManager{

 private Sample01 mockSample01;

 /** DIのモック  */
 @Mocked
 private SampleDao sampleDao;

 @Test
 public void testDoTest() {

                mockSample01 = getBean(Sample01.class)

  new NonStrictExpectations() {{
   Deencapsulation.setField(mockSample01, sampleDao);
        }};

        mockSample01.test();

 }

}

さて、JMockitは内部で「new」しているクラスも勝手にモック化する超強力ツールであることは第4回でご紹介しましたが、
DIコンテナの場合は「@Autowired」のアサーションで自動セットしており、内部でnewしているわけでは無い為か、普通にやってもモック化されません。

厄介ですね。

こういう場合は、荒技を使います。


  1. とりあえず普通にコンテナ経由でクラスを生成して、@Autowiredにより自動セットして貰う。
  2. 自動セットされた後でモックに上書き!!

privateフィールドを上書きするという超荒技です。


「Deencapsulation.setField(mockSample01, sampleDao);」というソースが、その上書きを行っている箇所です。


簡単に使えますね。
Javaクラス設計の根底をひっくり返す裏技ですが、そこはJMockitの黒魔術ということでご了承下さい。

終わりに

引き続き、現場からのフィードバック記事を連載します。

リクエストがあればこのような形で順次お答えしていきたいと思います。

2014年4月28日月曜日

【追加】最強モックツール JMockit その12 カバレッジオプション

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

只今、私の現場ではこのブログを参考資料としてJUnit開発を進行中!!

そこで現場の皆様からリクエストが上がりましたので、ちょっと昔に巻き戻って情報を補強させて頂きます。

カバレッジオプション

JMockitにはカバレッジ出力機能が備わっている事は第2回でご紹介致しました。

JMockitのカバレッジ出力は簡単でして、jarファイルをインポートすれば勝手に出て来ます。
なので前回の記事では簡単に済ませてしまいましたが、ちょっとカスタマイズしたいという事情が出て来ましたので、オプションにてご説明しましょう。

カバレッジについて記述されている公式ページは以下になります。

機能のご紹介としては、概ね以下のような内容です。

coverage-output

カバレッジの出力モードのオプションです。
普通に実行するとHTMLファイルのカバレッジ結果が出力されますが、モードを設定することによって「シリアル」「マージ」といった機能が解禁されます。

「シリアル」「マージ」というのは、JUnitテストの分割で使用するパラメータです。

普通、JUnitのカバレッジというのは1回で全部出力しなければなりませんが、「シリアル」で実行するとcoverage.serという途中ファイルが出力されます。
これがシリアルファイルです。

複数に出力したシリアルファイルを合体させてHTML出力する機能を「マージ」と言います。

基本、JUnitは1クリックで全部出力するのが良いですが、「シングルトン」とか使ったプログラムですと、どうしても1回ではカバーしきれない場合があります。
そういう時に「シリアル」「マージ」のオプションを使うのが良いでしょう。

coverage-outputDir

カバレッジ出力先を指定するオプションです。
普通に実行すると実行パス直下に「coverage-report」というフォルダが出力されますが、違う場所に出力したい場合に使用します。

coverage-srcDirs

カバレッジ出力対象とすろソースフォルダのパスを指定するオプションです。
デフォルトでは「src」の配下を対象とするので、プロジェクト作成時に「srcフォルダ」「testフォルダ」の2つに分割するのが基本構成であるのは、第2回でご紹介したとおり。

しかし何らかの事情でsrcフォルダ配下にソース、という構成が取れない場合は、このオプションを使用して対処します。

coverage-classes

カバレッジ対象とするJavaファイルを正規表現で絞り込む機能です。
通常ですと全Javaファイルを走査してカバレッジを出力しますが、このオプションによって「特定パッケージだけ」等の対応が可能になります。

coverage-excludes

こちらはcoverage-classesの反対で、正規表現で特定Javaソースを除外する機能です。

coverage-metrics

カバレッジの網羅パターンを指定します。
デフォルトで実行すると「C0(行網羅)」でカバレッジが出力されますが、
オプションを指定することで「C1(分岐網羅)」「C2(条件網羅)」のモードに切り替えることが出来ます。

coverage-check

カバレッジ率をチェックするオプションです。

普通に実行すると単にカバレッジが出力されるだけですが、オプションを指定することにより「指定数値よりカバレッジが低い場合」に警告を出してくれるようになります。

オプション実行

如何でしたでしょう?
最強モックツールに相応しい、痒いところにまで手が届く充実したオプションシリーズですね。

しかし、基本的には「オプションを使わなくても良いようにプロジェクトを運用する」のが大事になるかと思います。
オプションを使用するということは、それだけ複雑になるということですから。。。

しかし、どうしてもオプションを指定して実行しなければならなくなることもありますので、Eclipseでオプション指定実行をする手順もこの記事にて掲載します。

実行の構成から指定する


EclipseからVM起動オプションを設定する場合、基本は以下のように実行の構成から指定することになるでしょう。


  • 右クリック⇒実行の構成⇒引数

ここから、「-Dcoverage-metrics=all」のように、頭に「-D」をつけた引数を入力すれば、それでオプション付きで実行したことになります。


しかしですね、これをやると「ファイル毎に起動構成を作らなければならない」という欠点があります。

JUnitテストなんて、色々なテストケースを何度も何度も実行するものですから、毎回毎回オプションを気にしながら実行するのは面倒極まりないと思います。

そこで、私としては「デフォルトVM引数」を指定するのをオススメします。

デフォルトVM引数指定

Eclipseには「いつも同じVM引数をつけて実行する」というデフォルト指定モードがあります。
VM引数指定が必要な実行があるなら、こちらにすることで手間を省くことをオススメしたいです。

  • ウィンドウ⇒設定⇒Java⇒インストール済みのJRE

VM引数はVM単位で付与しますので、実行時に使用するJavaVMを選び、編集をクリックして下さい。
すると、「デフォルトのVM引数」という項目があるので、ここに入れて実行すればOKです。



こうすることで、最初にデフォルト指定しておけば、いつも同じ設定で実行出来るようになりますので、テストコード開発も捗るでしょう。

終わりに

久しぶりのJUnit記事になりました。

現在、私は現場でこの記事をベースにJMockitを使ったユニットテスト開発を行っていますが、やっぱりJMockitは強力ですね。
出来ないことが無いというくらいに、何でもJMockitで貫き通せています。

次回も現場で出て来た要望への対応、「プライベート変数書き換え」をお送りします。

現場で使っているライブラリ「Spring」のDI機能をモック化する為の特殊対応です。

2013年10月30日水曜日

最強モックツール JMockit その11 Eclipseプラグイン

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

長く続いた「JMockit」の記事も本日が最終回です。
JMockitは非常に多機能なライブラリなので他にもまだまだ機能はあるのですが、大抵の開発ならば前回までの内容で対応出来ると思います。
何回か使ってみて、『もっとこういう機能が無いかな?』という状況になった時、改めて探してみるのが良いかと思います。

さて、最後はチラッと小ネタをご紹介します。

Eclipseプラグイン


JMockitには、Eclipse上での入力を支援してくれるプラグインがあるようです。


導入すると、「ctr + スペース」で以下のように入力候補が出てくるようになります。


他にも、「書き間違えている場合に赤くエラーを出す機能」「テスト対象になっているメインソースのジャンプする機能」などがついています。

別に導入必須という程の大した機能ではありませんが、こうした「ちょっと便利な機能」も忘れず押さえておくことで、日頃の開発効率を高めていくようにしたいものですね。

これからのJMockit~JMockit2~


何と、Googleの開発サイトでは「JMockit2」の開発が始まっているらしいです。


まだ開発途中ですが、『JMockit1とは違う視点から、もっとシンプルで優れたモックライブラリを作る!!』との触れ込みです。

現時点で十分に優れたライブラリかと思いますが、確かに調べていて少し難しいと感じることもあり、「シンプルさ」にはまだ改善の余地があるのかもしれません。

完成がいつになるかは分かりませんが、Java単体開発者としては耳寄りな状況として要チェックです。

首を長くして完成を待ちたいと思います。

終わりに


これにてJMockitの連載は終了です。

連載を終えるに辺り、JMockitに関する所感を述べるならば、上にもありますが「結構難しい!!」ですね。
そもそも、モックという発想自体が黒魔術ですので、何年もJava開発を行って基本を押さえている技術者ならば兎も角、経験の浅い人が触ったら混乱することになると思います。

また、『チームの全員がJMockitに精通している』という超精鋭チームを用意することも至難でしょう。

というわけで、「せっかく覚えたJMockitだ。ぜひ使いたい!!」という流行る気持ちは押さえて、

  • モックを使わなくてもテスト出来るクラス設計にする。
  • どうしても必要な例外部分だけ、JMockitを使用する。

といった程度の水準で導入するのが一番現実的ではないでしょうか。

例えば「正常系は普通にテストする。異常系はJMockitを使う」など、開発指針のレベルで適切な運用方法を考える所からがJMockitの始まりかと思います。


以上でJMockitの連載を終わります。
ご愛読ありがとうございました。

2013年10月21日月曜日

最強モックツール JMockit その10 コンストラクタ差し替え

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

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

今回はコンストラクタの差し替えです。

コンストラクタの差し替え

前回は「メソッドを丸ごと差し替え」を行いましたが、
今回はその延長線で「コンストラクタ」の差し替えです。

コンストラクタは普通のメソッドとは違うということか、他のモックツールを見てもコンストラクタには非対応ということが多いようです。
しかしコンストラクタのモック化を必要とするシチュエーションも少なからずあるもの。
例えば、バッチの開発。偶に見るのですが、コンストラクタで環境設定ファイルを読み込む作りになっていることがあるのです。
しかも、前もって用意されている正規の環境設定ファイルは本番実行用で、テストの時はテスト用ファイルを読み込まなければならない、という場合も結構あります。

しすれmそういう現場では必ず以下のような会話で盛り上がります。


「あれ? テストが動かないぞ?? どうなってんの???」⇒「設定ファイルの差し替えを忘れてるんですよ」


これを何回でも繰り返すのが人間というものです。どの現場に行っても100%コレでした。
というわけで、ファイルの差し替えとか設定値の切り替えとか、そういう作業はJMockitを使ってテストソースの中に埋め込んでおく方が楽です。
ぜひ使ってみてください。

では、JMockitにおけるコンストラクタ差し替えの記述方法をご紹介しましょう。
まず、サンプルとして以下のようなコンストラクタを含んだクラスを作成します。

public class ConstructorSample {

 public ConstructorSample() {
  System.out.println("★コンストラクタ処理★");
 }

}

サンプルですのでprintlnのみの処理ですが、本番ではここで色々処理しているという想定です。
これに対し、テストソースの方は以下のように書きます。

public class ConstructorSampleTest {

 @Test
 public void test() {
  new MockUp<ConstructorSample>() {

        @Mock void $init() {
         System.out.println("★コンストラクタ差し替え★");
        }
  };

  new ConstructorSample();
 }

}

コンストラクタの場合はメソッド名の指定が普通には指定できませんので、『$init』という特殊記号を使うわけです。

書き方の要領は前回と全く同じですので、『$init』を使うのだということを知っていれば簡単に使うことが出来るでしょう。


終わりに


JMockitは高機能なライブラリなので連載が長引きましたが、
今日までの記事で殆どのケースに対応可能だと思います。

次回は最終回、JMockit開発を支援するEclipseプラグインをご紹介します。

2013年10月15日火曜日

最強モックツール JMockit その9 メソッド丸替え

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

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

今回はメソッドの中身の丸ごと差し替えです。

メソッド全体の差し替え

今までご紹介してきたモック機能というのは、『元々のメソッドを実行しないで、仮の値を返す』というものです。

例えば、ログインチェック機能の場合、
本来の機能であれば『ログインIDとパスワードをチェックして、正しい場合はtrue、間違っている場合はfalseを返す』だったとします。
しかし、モック化することによって『いつも固定でtrueにする』とか、そういう感じです。

この場合、元々のメソッドは実行していませんので、DBにアクセスに行っていません。
ただ単に『true』という値が帰ってくるだけです。

このように、『返り値だけあればいい』という状況であれば今までのやり方でOKです。
しかし、そうではなくて、『別の処理をちゃんと実行して欲しい』という場合は、これではダメです。

例えば、以下のような状況です。

  • DBに間違った値が入った時のテストだから、本来の機能はモック化して潰し、代わりに間違った値をインサートさせたい。
  • メール送信機能のテストなんだけど、メール送信処理自体は実際にはメールを送信しなくていいからモック化する。でも、同時に行っている一時ファイル削除処理は行っておく必要がある。

このように、『ダミーの値を返すだけではなくて、違う処理をやりたい』という場合です。
こういう場合は、元々のメソッドをぶっ潰して、違う機能を書いてしまいましょう。

『メール送信後、ファイルを削除する』機能をモック化するというシチュエーションを想定します。
(サンプルなので、printでその処理を行ったという想定とします)

public class MailManager {
 public void sendMail(){
  System.out.println("★メールを送信する。★");
  System.out.println("送信した添付ファイルを削除する。");
 }
}

JUnitは単体テストですが、メール送信はプログラムだけでなくメールサーバも必要とします。
こういう風に違うサーバを必要とする処理のテストは結合テストで行って、単体テストフェーズではモックで済ます、というのは常套手段と言えるでしょう。

しかし、同時に行っている添付ファイル削除処理は実施しなければ都合が悪いということもあるでしょう。
もしくは、『メール送信は成功したけど、ファイル削除は失敗した』という状況を作りたかったりするなど、細かい要望は色々あります。

そういう場合、モックでメソッドを新規作成してしまえば良いのです。

以下がそのやり方です。

@Test
 public void testCreateFile() {
  new MockUp<MailManager>() {
   @Mock
   void sendMail() {
    System.out.println("ファイル削除(差し替え)");
   }
  };
  //メール送信
  MailManager manager = new MailManager();
  manager.sendMail();
 }

JMockitの場合、今回新規登場の『MockUp』というクラスを使うことで実現できます。

使い方も分かりやすいかと。
MockUpクラスの中で同じ名前のメソッドを書いて、@Mockをつければ、それだけで後は自動でモック化されます。

直感的に分かるようによく出来ているライブラリだと感心してしまいます。

終わりに


今回ご紹介したサンプルは、実際のところは回避可能でもあるんですよね。
だって、『メール送信』と『ファイル削除』を別々のメソッドにして、メール送信メソッドだけモック化すれば良いのですから。
しかし、実際の開発だとクラス設計がJUnitのことまで考えられていなくて、こういう作りになっていることもしばしば。
不本意ながらに結構お世話になってしまう機能かもしれません。

もしくは、『ファイル削除失敗』みたいに現実には実現出来ないシチュエーションを作るにも便利です。

代替機能を自分で作る手間はありますが、こんな風に自由に作り込める機能は大変助かりますね。

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

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活用法のサンプルをご紹介します。

2013年8月26日月曜日

最強モックツール JMockit その2 インストール編

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

現在はモックツール「JMockit」をご紹介しています。
今回はインストールと動作確認まで行ってみましょう。

ダウンロード&インストール

まずはJUnit本体のダウロードです。

何度も書いていますが、Eclipseの標準JUnitだと不足がありますので、自前でJUnitセットをダウンロードして下さい。


次にJMockitをダウンロードします。
この記事を執筆している現時点での最新は1.4です。


ダウンロードしたzipファイルを解凍すると「jmockit.jar」と「jmockit-coverage.jar」の二つのjarファイルがあります。
それぞれ、jmockit本体とカバレッジ出力ツールです。

今回の連載ではカバレッジも紹介しますので、両方とも導入しましょう。

ダウンロードしたら、これら全部をEclipseのクラスパスに入れて完了。
単にjarファイルを導入するだけの簡単な作業です。


では無いんですよね!!


何と、jmockitは「クラスパスをJUnit本体より先にしなければならない」という制約があります。

普通の開発ではクラスパスの順番など意識しないと思いますが、今回は違います。
Javaのプロジェクトに複数のjarファイルを導入する際、異なるjarファイル間で同じクラスが存在していた場合は、先に書いた方のjarファイルの方が優先されます。

つまり、jmockitはJUnit本体より先にクラスパスを書いて、JUnit本体の一部を潰して機能を実現しているわけです。
導入段階から早くも黒魔術です。

Eclipseの場合、プロジェクトのプロパティから「順序およびエクスポート」の画面に進むことでクラスパスの順番を変更出来ます。

私の環境では以下のような状態になりました。



これでセットアップ完了です。

動作確認

次に細かいことは抜きにして、まずは動作確認してみましょう。

本体ソースの方は、以下のようにただ「テスト」という文字列を返すだけのクラスとメソッドを作ります。

public class Sample01 {
 public String doTest() {
  return "テスト";
 }

}

これに対し、jmockitを使って「テスト」という返り値を「モック」に差し替えてみたクラスが以下です。
/**
 *
 */
package jp.co.net.genesis.sample;

import mockit.Mocked;
import mockit.NonStrictExpectations;

import org.junit.Test;

public class Sample01Test {

 /** モック  */
 @Mocked
 private Sample01 mockSample01;

 /**
  * {@link jp.co.net.genesis.sample.Sample01#doTest(java.lang.String[])} のためのテスト・メソッド。
  */
 @Test
 public void testDoTest() {

  new NonStrictExpectations() {{
   mockSample01.doTest(); result = "モック";
        }};

        System.out.println(mockSample01.doTest());

 }

}

ひとまず実行してみて下さい。
「モック」という文字になったかと思います。
これでjmockitの動作確認が出来ました。

上記ソースにありますように、モック化するクラスをフィールド変数に定義し、「@Mocked」というアサーションを付けるのがお約束です。
そして、具体的にどんな機能に差し替えるのかを書いているのが、「NonStrictExpectations」の内部の部分です。

意味は以後の連載でご説明するとして、今回の所は動きを確認出来ればOKでしょう。

カバレッジ


私の環境だと、Eclipseのコンソールには以下のように表示されます。


どうやらカバレッジも出力してくれたようですね。
導入段階で「jmockit-coverage.jar」を入れましたので、自動的にカバレッジまで出力してくれたようです。

さっそく見てみましょう。

まず、index.htmlを開くと、以下が表示されます。


どうやら全体のカバレッジ率を出してくれる画面のようです。
今回の場合「0%」になっていますが、これは本体ソースをモックで潰して実行してしまった為、本体ソースは全く実行していないからです。
このように、jmockitはちゃんと「本体ソースを通った箇所」「モックで潰した箇所」を別々にカウントしてくれますので、カバー漏れ等は発生しません。

クラス名をクリックすると、カバレッジ詳細が表示されます。


画像は赤一色になっていますが、通過した箇所としていない箇所をちゃんと別々の色で表示してくれますので、分かりやすいです。

これにてカバレッジの出力も確認出来ました。

注意点


ここで一つ注意を入れておきたいと思います。
jmockitの公式サイトを見ると、以下のような文章が。

HTML reports: a multi-page HTML report is written in the "coverage-report" directory, under the current working directory (a different output directory can be specified if needed). The directory is created if it doesn't yet exist; its contents are overwritten if previously generated. The report will include pages containing all Java source files covered by the test suite. By default, the tool looks for ".java" source files inside all directories of name "src" found directly or indirectly under the current working directory; any intermediate sub-directories between "src" and the top-level package directory, such as "src/java" for example, are also searched.

何と、カバレッジを出力する際は「フォルダ名」の指定があるようで、デフォルトでは「src」の配下がソース本体として扱うだそうです。

つまり、ソースフォルダの構成が影響します。

src
 L main
    Ljava
    Lresource 
 L test
    Ljava
    Lresource

みたいにsrc配下に「main」と「test」を置いてしまうと、testソースまでカバレッジ対象になってしまい、HTML出力にゴミが混ざってしまいます。

また別のパターンとして、

main
  Ljava
  Lresource 
test
  Ljava
  Lresource

みたいにsrcという名前のフォルダが無いと、カバレッジが出力されません。

では私はどうしたかと言うと、

src
  Ljava
  Lresource
test
  Ljava
  Lresource

こういう構成にしてみました。
これなら綺麗に出力出来るようです。

「カバレッジが出ないぞ!?」と困っている人は大概はコレだと思いますので、ご確認下さい。


終わりに


ひとまずこれでJMockitのインストールが出来ました。

次回からはJMockitの各種詳細機能の検証に進みます。

高機能なライブラリですので長くなると思いますが、最後までお付き合い下さい。

2013年8月17日土曜日

最強モックツール JMockit その1

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

今回から新シリーズ「モック編」の始まりです。

タイトルに「最強」などと派手な名前をつけてしまいましたが、実際に最強と呼び声の高い強力なJUnit支援ツールがあります。
それがモックツール「JMockit」です。
しかし、第一回である今回はJMockit個別の話の前に「モックとはなんぞや?」という所からご説明しましょう。

百聞は一見に如かず。まず、例をお見せします。


まず、以下のように「ジェニシス」という文字を返すだけの、シンプルなクラスを作ります。

public class Simple {
 public String getStr(){
  return "ジェニシス";
 }
}

これに対してテストケースを作り、System.out.printlnで出力結果を見てみます。
public class SimpleTest {
 @Test
 public void testGetStr() throws Exception {
  Simple simple = new Simple();
  System.out.println(simple.getStr());
 }
}

まあ、当然ながらコンソールには「ジェニシス」と出ます。当たり前です。

これに対し、モックを入れてみます。

public class SimpleTest {
 @Mocked
 private Simple mockSimple;
 @Test
 public void testGetStr() throws Exception {
  new NonStrictExpectations() {{
   mockSimple.getStr(); result = "モックになりました。";
        }};
  System.out.println(mockSimple.getStr());
 }
}

すると、コンソールには何故か「モックになりました。」と出てきてしまいました!?

不思議ですね。。。

本体ソースを見る限りはどうやったって「simple.getStr() == "ジェニシス"」以外にありえないのですが、
実際には違う値が出てきてしまいました。。。

このように「本体ソースの機能をダミーに差し替えて実行する」というハッキングみたいなことをする。
それがモックです。
テスト上の役割としては「スタブ」に近いものとなります。

定義の上では厳密言えば「スタブ」と「モック」は違うものなのですが、下位機能を本体とは別のソースで置換するという点は同じです。

本体ソースではテストが難しいケースに対し、ダミーの値に差し替えてテスト出来るというのがスタブの存在意義です。

モックの自作


上記のモックソースはJMockitを使った結果ですが、別にツールを使わなくても自分でモックすることは可能です。

public class SimpleTest {
 @Test
 public void testGetStr() throws Exception {
  Simple simple = simpleMock();
  System.out.println(simple.getStr());
 }
 private Simple simpleMock(){
  Simple mock = new Simple(){
         public String getStr(){
          return "モックになりました。";
         }
        };
        return mock;
 }
}

「Simpleを継承した別クラス」を作って、メソッドをオーバライドした上で差し替えてるわけです。
以下の部分がそれに該当します。

Simple mock = new Simple(){
  public String getStr(){
    return "モックになりました。";
  }

普通にクラスを継承して作るのでは無く、コードの中でクラスを作っています。
こういう書き方をしたクラスを「無名クラス」と言います。

今回私が作ったサンプルモックは低機能かつSimpleクラス専用ですが、JMockitを初めとした各種モックツールは、
これをどんなクラスにでも動的に対応してくれます。

動的対応には「リフレクション」という文字列からクラスを動的に生成する機能を使うのですが、
そこまで説明すると記事が長くなり過ぎるので割愛します。

それに、プライベートメソッドも容赦なく変えたり、クラスの中でnewしているクラスまで差し替えたりと何でもござれ。

でも、普通はこんなコーディングしませんよね。
このように、モックツールというのはJavaの中でも『黒魔術』とまで称される禁断のテクニックをフル活用した裏技ツールなのです。

モックツールの選別


さて、モックツールは世の中に沢山ありますので、どれを使うのが良いかという問題があります。
有名所と言えば以下辺りなんじゃないでしょうか?

  • EasyMock
  • Mockito
  • jMock

しかし、あえて私はタイトルにもありますように、JMockitを推したいと思います。
機能の豊富さが理由です。

以下のサイトをご覧下さい。


JMockitの公式サイトなので多少の贔屓が入っているのかもしれませんが、
やはり機能面では最強と見て間違い無いようです。

一方、「機能が多ければ使いこなすのも難しいのではないか?」という印象を受けるのも事実です。
しかし、逆に言えば使いこなせるだけの知識があれば無敵!!

ここは一つ、気合い入れて研究してみようではありませんか。

次回


次回から本格的にJMockitについて解説。
最初はインストール編です。