<< <
1
2
3
4
5
6
7
8
9
10
> >>
先日発売された日本初のWicket本が届きました。
著者の矢野さん、お疲れさまでした。
レビューに参加したということで、名前も載せていただきました。
技術本のレビューという、貴重な体験をさせてもらった上、本を頂けるとは感謝しきりです。
今夜は紅茶で祝杯です。

これまでモックオブジェクトを作成する EasyMock や jMock を見てきました。
モックオブジェクトをパラメータとしてテスト対象に渡せる場合は、問題ないのですが、テスト対象のクラスの中で new されていると、一気にテストは困難になります。
jMockit は 実行時にクラスをモックオブジェクトへ差し替える機能を提供するツールです。
バイトコード操作の為のJVMへのコマンドライン引数、 -javaagent を用いてクラスの差し替えを行います。
Eclipseの場合はJREの設定を変える必要があります。JVMの引数に -javaagent:jmockit.jar を追加します。
設定自体は簡単なのですが、この点が壁になるかもしれません。

テスト対象は以下の通りです。MockSampleクラスのコンストラクタでサービスクラスを初期化しています。
この様に、テスト対象のクラス内で new されたクラスはモックオブジェクトを適用するのが困難です。
jMockitはこういったクラスをモックオブジェクトに差し替える機能を提供しています。
JVMの引数をいじるため、Eclipseを設定したり、Mavenを使っている場合も一工夫がいる点が面倒ですが、
一度設定してしまえばかなり完結にテストをかける強力なツールです。
ここまでで EasyMock と jMock の簡単な使い方を見てきました。
まだまだ使いこなしてはいないのですが、ここまでの知識をまとめておきたいと思います。
まず、Mock と Stub の違いについて。
EasyMock とか jMock に触れる前は、Mock と Stub が自分の中で明確に区別できていませんでした。
Wikipediaによると両者は異なるもので、Mock は Stub と比較して正しく利用されているかを検証する機能が付加されているとの事です。
確かに今まで Stub と認識していたものは、「呼ばれたときに値を返す」部分を代用する様なものでした。
しかし EasyMock や jMock はメソッドが想定されたパラメータ、順序で呼ばれるかを検証することができました。
この点で Stub とは 区別される物のようです。Mock が Stub の一種と言うことですね。
さて、 EasyMock、jMock ともに機能に大きな違いはなさそうです。
また、基本的な使い方の比較をコードでしてみます。
まず、Mock を作る部分。
EasyMockの場合。
jMockは想定される呼び出しを context.checkingメソッド内のExpectationsクラス初期化子に記述し、context.checking〜context.assertIsSatisfiedメソッドで囲われた部分を検査します。
個人的には jMock の書き方の方が決められた場所が明確で好きなのですが、好みの問題かもしれません。
他にも "Mock" という文字列を含むソリューションがありましたが、試すまでには至りませんでした。
まず rMock なのですが、対応していないのかもしれないのですが TestNGにからの利用方法がわからなかったので、試しませんでした。
ただ、ドキュメントを読む限り BDD的な要素を絡めているようでおもしろそうではあります。
あと、ClassMock と呼ばれるものもありましたが、こちらは Mock と呼ばれるには必要な、正しく呼ばれているかを検証する機能がなく、簡単にクラスを作るための仕組みを提供している様です。
Mockとしての機能は他のソリューションに委ねている様で、ドキュメントにも jMock と連携した場合の例が載せられています。
Mock の使いどころとして浮かぶのは Servlet とかのテストなのではないでしょうか。
つまり擬似的な実装クラスを作るのが面倒な部分です。
Wicket だと WicketFilter クラス何かは Mock を使ったテストなんかは有効なのではと思います。
jMockではインターフェース以外のクラスについてモックオブジェクトを作れましたが、EasyMockについてもEasyMock Class Extension を導入することで実現できます。
まずEasyMockのサイトからライブラリをダウンロードするなり、Mavenを使っているならpom.xmlを以下のように編集します。
以前、インターフェースに対してモックオブジェクトを作成する部分はこんな感じでした。
具体的には、org.easymock.EasyMock.createMock から org.easymock.classextension.EasyMock.createMockメソッドを使用するようにimport文を変更します。
他にも replyメソッドやverifyメソッドもclassextensionパッケージ下のものを使うことになるので、インポート文に下記を加えておくとよいでしょう。
これでjMockと同様に、インターフェース以外のモックオブジェクトの作成が可能になります。
前回はEasyMockについて簡単なサンプルを作って見ました。
今回はjMockで同じような事を試して、EasyMockとどう違うか見ていきます。
まずjMockのサイトからライブラリをダウンロードするなり、Mavenを使っているならpom.xmlを以下のように編集します。
想定される呼び出しの記述にインスタンス初期化子を使っている所が個人的には見やすく感じました。
戻り値の指定も同様にインスタンス初期化子の中に記述します。
jMockでは内部的に使用しているProxyクラスに関して、reflectionを使う場合とcglibを使う場合に切り替ることができます。
cglibを使うことで、インターフェースのないクラスのモックオブジェクトを作成出きるようになっています。
インターフェースのないクラスのモックオブジェクトを作る場合は、pom.xmlに下記を追加します。
この様に、jMockはEasyMockと比べるとインスタンス初期化子で記述する点や、インターフェースがなくてもモックオブジェクトを作成できる点で、優れている所も多そうです。
ユニットテストをやってると、クラスの依存関係によってテストクラスの作成が難しいことがよくあります。
今の現場は特にユニットテストを作成する事が多いので、この障害にぶち当たることが頻繁です。
こういったケースではモックオブジェクトを作成したりするのですが、Javaではモックオブジェクトを作るためのライブラリーがいろいろあるようです。
ざっと見た限り、jMock, rMock, djUnit(Virtual Mock) EasyMock等が上げられます。
最終的にはこれらの違いを考察したいのですが、今回はEasyMockを試してみます。
まずEasyMockのサイトからライブラリをダウンロードするなり、Mavenを使っているならpom.xmlを以下のように編集します。
テスト対象のクラスは以下のようなものを用意しました。
コンストラクタでIServiceクラスをインターフェースとするクラスを受けとります。
このクラスのメソッドを実行するだけのメソッドexecute()、getName()があります。
createMock(IService.class);のようにしてモックオブジェクトを作成します。この部分はGenericsを使っているため、キャストもなくすっきりしていますね。
次に、生成したモックオブジェクトを何らかの方法でテスト対象のクラスに注入して、適切に呼ばれているかをテストしていきます。
想定される呼び出しを記述し、replay(mock)〜verify(mock)の間で想定通りに呼び出されているかをテストしていきます。
また、モックオブジェクトが呼び出された時の戻り値も指定することができます。
このように、モックオブジェクトは、それ自身が正しく呼ばれているか検証することができます。
ただ、内部的にはjava.lang.reflectパッケージのProxyクラスを用いていることから、インターフェースの存在が必須になっているようです。
この点がEasyMockの弱点かなと思います。
また、テスト対象のクラスにモックオブジェクトを登録する必要があります。
うまく登録できる入り口があればよいのですが、この点がテストクラスの作成を困難にすることが多々あります。
この点はモックに限らないことですが。
次はjMockを見ていく予定です。
この前m2eclipseを使ってみたのも、TestNGを試してみるためでした。
TestNGのマルチスレッドなテストを試してみたかったんです。
ちなみにJunitはずっと使ってるけど、3.x系列しか使った事がなかったので、アノテーションベースのテストクラスも初体験でした。
アノテーションに関してはJunit4.x系列なら扱えるんですけどね。
さてさて、マルチスレッドなテストを試すため、下記のようなスレッドアンセーフティーな実装をしてみました。
下記の例ではスレッド数3で、実行回数を10回としています。
エラーメッセージを見て戸惑いました。
今更ながらlibataを導入した
久々に我が家のデスクトップ Gentoo マシンをいじったので、メモしときます。
我が家のデスクトップは Raid1 構成の IDE ハードディスクをくっつけてます。
マザーボードは Albatron PX915G。
Raidコントローラーは IT8212 です。
で、新しいkernelを再構築して起動すると、ハードディスクへのアクセスが恐ろしく遅くなってしまいました。
dmesg の出力を見ると、"dma not selected" と出ています。
dmaが無効になってしまっているようです。
ちなみにmake menuconfigでkernelの再構築時にコンパイルしたドライバーは以下の通りです。
同じような事で悩んでいる人もいるようで、kernelのメーリングリストでも数年前に話題になっていたようです。
Re: IT821x: no DMA since 2.6.21
上記で組み込んだドライバーはメンテナンスされていないのでlibataを使うのが良いとか書いてあります。
libataはSerialなハードディスクだけの話かと思っていたのですが、parallelなハードディスクでも利用できるんですね。
make menuconfigで上記のドライバーをはずして、下記のドライバーを組み込みます。
我が家のPCでは IT8212経由のハードディスクは hde として認識されていました。
libataを使うことで、sda に変わったことが今回の変更の副作用です。
長らくハードディスクデバイスと言えばhd〜だったので、暫く違和感がありそう。
(OpenBSDのwd〜も暫く慣れなかったけど)
ハードディスクデバイスが /dev/sda に変わったので /boot/grub/grub.conf と /etc/fstab も忘れずに変更しましょう。
我が家のデスクトップは Raid1 構成の IDE ハードディスクをくっつけてます。
マザーボードは Albatron PX915G。
Raidコントローラーは IT8212 です。
で、新しいkernelを再構築して起動すると、ハードディスクへのアクセスが恐ろしく遅くなってしまいました。
dmesg の出力を見ると、"dma not selected" と出ています。
dmaが無効になってしまっているようです。
ちなみにmake menuconfigでkernelの再構築時にコンパイルしたドライバーは以下の通りです。
Device Drivers --->
ATA/ATAPI/MFM/RLL support --->
IT821X IDE support
同じような事で悩んでいる人もいるようで、kernelのメーリングリストでも数年前に話題になっていたようです。
Re: IT821x: no DMA since 2.6.21
上記で組み込んだドライバーはメンテナンスされていないのでlibataを使うのが良いとか書いてあります。
libataはSerialなハードディスクだけの話かと思っていたのですが、parallelなハードディスクでも利用できるんですね。
make menuconfigで上記のドライバーをはずして、下記のドライバーを組み込みます。
Device Drivers --->
Serial ATA (prod) and Parallel ATA (experimental) drivers
IT8211/2 PATA support
我が家のPCでは IT8212経由のハードディスクは hde として認識されていました。
libataを使うことで、sda に変わったことが今回の変更の副作用です。
長らくハードディスクデバイスと言えばhd〜だったので、暫く違和感がありそう。
(OpenBSDのwd〜も暫く慣れなかったけど)
ハードディスクデバイスが /dev/sda に変わったので /boot/grub/grub.conf と /etc/fstab も忘れずに変更しましょう。
Wicket本が届きました!
先日発売された日本初のWicket本が届きました。
著者の矢野さん、お疲れさまでした。
レビューに参加したということで、名前も載せていただきました。
技術本のレビューという、貴重な体験をさせてもらった上、本を頂けるとは感謝しきりです。
今夜は紅茶で祝杯です。

Wicketでパネルのユニットテスト
WicketにはWicket Testerと呼ばれるテストツールが付属しています。
サーブレットコンテナを起動せずにテストが出来る優れものですが、
今回、Wicketのパネルをテストしたときに、ちょっと癖があったのでメモしておきます。
Wicketのパネルとはいろんなコンポーネントとマークアップを一まとめにした部品です。
標準だとページャーを表示する部品、PagingNavigator なんかがあります。
パネルを使いまわすことができれば、コンポーネントの部品化は非常に進むと思います。
「部品化」が机上の空論にならないためにも、パネルとテストはどんどん活用したいですね。
今回は単純に現在時刻を表示するパネルを作ってみました。
テスト対象のパネルにはラベルが2つあるだけの単純なものです。
リポジトリにも置いてあります。
これをテストするコードは下記になります。
これもリポジトリにも置いてあります。
このクラスのgetTestPanelメソッドの戻り値として、テスト対象クラスのインスタンスを返すようにします。
パネルのwicket:idはDummyPanelPage.TEST_PANEL_IDとなります。このidを前提としてテストコードを書きます。
パネルのwicket:idがDummyPanelPage.TEST_PANEL_IDで固定になっている事を記載しているドキュメントは見つけられませんでした。
この点はドキュメントに書くべき部分でしょうね。
サーブレットコンテナを起動せずにテストが出来る優れものですが、
今回、Wicketのパネルをテストしたときに、ちょっと癖があったのでメモしておきます。
Wicketのパネルとはいろんなコンポーネントとマークアップを一まとめにした部品です。
標準だとページャーを表示する部品、PagingNavigator なんかがあります。
パネルを使いまわすことができれば、コンポーネントの部品化は非常に進むと思います。
「部品化」が机上の空論にならないためにも、パネルとテストはどんどん活用したいですね。
今回は単純に現在時刻を表示するパネルを作ってみました。
テスト対象のパネルにはラベルが2つあるだけの単純なものです。
リポジトリにも置いてあります。
public class TimerPanel extends Panel {
/** serialVersionUID */
private static final long serialVersionUID = 1L;
public TimerPanel(String id) {
super(id);
add(new Label("label", getString("label")));
IModel<String> model = new AbstractReadOnlyModel<String> () {
/** serialVersionUID */
private static final long serialVersionUID = 1L;
@Override
public String getObject() {
DateFormat format = DateFormat.getTimeInstance();
return format.format(new Date());
}
};
Label timerLabel = new Label("timer", model);
timerLabel.add(new AjaxSelfUpdatingTimerBehavior(Duration.ONE_SECOND));
add(timerLabel);
}
}
これをテストするコードは下記になります。
これもリポジトリにも置いてあります。
public class TestTimerPanel {
private WicketTester tester;
@BeforeMethod public void setup() {
tester = new WicketTester();
}
@Test public void initDisplay() {
tester.startPanel(new TestPanelSource() {
private static final long serialVersionUID = 1L;
public Panel getTestPanel(String panelId) {
return new TimerPanel(panelId);
}
});
tester.assertComponent(DummyPanelPage.TEST_PANEL_ID, TimerPanel.class);
tester.assertComponent(DummyPanelPage.TEST_PANEL_ID + ":label", Label.class);
tester.assertLabel(DummyPanelPage.TEST_PANEL_ID + ":label", "現在の時刻");
tester.assertComponent(DummyPanelPage.TEST_PANEL_ID + ":timer", Label.class);
}
}
startPanelメソッドにTestPanelSourceのインスタンスを渡します。このクラスのgetTestPanelメソッドの戻り値として、テスト対象クラスのインスタンスを返すようにします。
パネルのwicket:idはDummyPanelPage.TEST_PANEL_IDとなります。このidを前提としてテストコードを書きます。
パネルのwicket:idがDummyPanelPage.TEST_PANEL_IDで固定になっている事を記載しているドキュメントは見つけられませんでした。
この点はドキュメントに書くべき部分でしょうね。
OpenBSD を PxeBoot でインストール
久々の更新になっちゃいました。
ノートPCが手に入ったのでOpenBSDをインストールすることにしました。
もらってから発覚したのですが、CDROMが壊れているマシンでした。インストールCDからインストールできない訳です。
それならと言うことで、以前から興味のあったPxeBootでインストールしてみることにしました。
構成としてはGentooマシンでdhcpdとtftpをホストしました。
やってみた感想としては、インストールするのにISOを焼く作業がなくなるし、設定もそんなに難しくないのでおすすめです。
インストール作業なんかはそんなに頻繁に行う作業ではないでしょうが、OpenBSDは半年に一回Updateがありますし。
参考にさせてもらったのは以下のドキュメント。
The Gentoo Linux alternative installation method HOWTO
OpenBSD FAQ 6.10 How do I boot using PXE? (i386, amd64)
ハマった所は、dhcpd.confで next-server を指定しないといけなかった所と、
PxeBoot環境では Grub は OpenBSD をうまく起動できない所でした。
dhcpdについてですが、設定ファイルでnext-serverを指定しないとサーバ機のIPアドレスを認識してくれませんでした。
また、OpenBSDをpxegrubで起動しようとしたのですが、起動時に下記のようなエラーがでて先に進めませんでした。
うまくいった手順についてですが、まずはGentoo機にdhcpdとtftpをインストールします。
Bootファイル設置用のディレクトリを作成します。
次に pxeboot を取得して、 /diskless 配下に置きます。
最終的な /etc/dhcp/dhcpd.conf の内容は以下のようになりました。
/etc/conf.d/in.tftp
クライアントをネットワークブートし、起動後に boot> プロンプトが表示されるので、 bsd.rd と入力します。
後は通常のインストールと変わりません。
ノートPCが手に入ったのでOpenBSDをインストールすることにしました。
もらってから発覚したのですが、CDROMが壊れているマシンでした。インストールCDからインストールできない訳です。
それならと言うことで、以前から興味のあったPxeBootでインストールしてみることにしました。
構成としてはGentooマシンでdhcpdとtftpをホストしました。
やってみた感想としては、インストールするのにISOを焼く作業がなくなるし、設定もそんなに難しくないのでおすすめです。
インストール作業なんかはそんなに頻繁に行う作業ではないでしょうが、OpenBSDは半年に一回Updateがありますし。
参考にさせてもらったのは以下のドキュメント。
The Gentoo Linux alternative installation method HOWTO
OpenBSD FAQ 6.10 How do I boot using PXE? (i386, amd64)
ハマった所は、dhcpd.confで next-server を指定しないといけなかった所と、
PxeBoot環境では Grub は OpenBSD をうまく起動できない所でした。
dhcpdについてですが、設定ファイルでnext-serverを指定しないとサーバ機のIPアドレスを認識してくれませんでした。
また、OpenBSDをpxegrubで起動しようとしたのですが、起動時に下記のようなエラーがでて先に進めませんでした。
panic: /boot too old: upgrade!Grubの設定を下記のようにすればよいとの情報があったのですが、現状には当てはまらないようです。
title=Diskless OpenBSD root (nd) kernel --type=openbsd /bsd.rd
うまくいった手順についてですが、まずはGentoo機にdhcpdとtftpをインストールします。
$ sudo emerge dhcp $ sudo emerge tftp-hpa
Bootファイル設置用のディレクトリを作成します。
$ sudo mkdir -p /disklessまず bsd.rd を取得して、 /diskless 配下に置きます。
次に pxeboot を取得して、 /diskless 配下に置きます。
最終的な /etc/dhcp/dhcpd.conf の内容は以下のようになりました。
ddns-update-style none ;
option routers 192.168.1.10;
option subnet-mask 255.255.255.0;
option broadcast-address 192.168.1.255;
option domain-name-servers 218.176.253.97;
next-server 192.168.1.12;
default-lease-time 120;
max-lease-time 120;
subnet 192.168.1.0 netmask 255.255.255.0 {
# クライアントにロードさせるブートファイルを指定
filename "pxeboot";
range 192.168.1.36 192.168.1.250;
}
tftpの起動オプションは以下のようになります。/etc/conf.d/in.tftp
INTFTPD_PATH="/diskless"
INTFTPD_USER="nobody"
INTFTPD_OPTS="-u ${INTFTPD_USER} -l -vvvvvv -p -c -s ${INTFTPD_PATH}"
クライアントをネットワークブートし、起動後に boot> プロンプトが表示されるので、 bsd.rd と入力します。
後は通常のインストールと変わりません。
jMockitで依存クラスをモックオブジェクトに差し替える
これまでモックオブジェクトを作成する EasyMock や jMock を見てきました。
モックオブジェクトをパラメータとしてテスト対象に渡せる場合は、問題ないのですが、テスト対象のクラスの中で new されていると、一気にテストは困難になります。
jMockit は 実行時にクラスをモックオブジェクトへ差し替える機能を提供するツールです。
バイトコード操作の為のJVMへのコマンドライン引数、 -javaagent を用いてクラスの差し替えを行います。
Eclipseの場合はJREの設定を変える必要があります。JVMの引数に -javaagent:jmockit.jar を追加します。
設定自体は簡単なのですが、この点が壁になるかもしれません。

テスト対象は以下の通りです。MockSampleクラスのコンストラクタでサービスクラスを初期化しています。
この様に、テスト対象のクラス内で new されたクラスはモックオブジェクトを適用するのが困難です。
jMockitはこういったクラスをモックオブジェクトに差し替える機能を提供しています。
public class MockSample {
private ServiceImpl serviceImpl;
public MockSample() {
this.serviceImpl = new ServiceImpl();
}
public String execute() {
return serviceImpl.echo();
}
public static class ServiceImpl {
public String echo() {
return "hoge";
}
}
}
テストクラスは以下の通りです。差し替えられたMockServiceImplが実行されます。
import mockit.Mockit;
import org.testng.annotations.Test;
import test.MockSample.ServiceImpl;
import static org.testng.Assert.*;
public class TestJMockitSample {
@Test public void jmockTest() {
// クラスの差し替え
Mockit.redefineMethods(ServiceImpl.class, MockServiceImpl.class);
MockSample sample = new MockSample();
assertEquals(sample.execute(), "Mocked!!!");
}
public static class MockServiceImpl {
public String echo() {
return "Mocked!!!";
}
}
}
コードはかなり簡単ですね。JVMの引数をいじるため、Eclipseを設定したり、Mavenを使っている場合も一工夫がいる点が面倒ですが、
一度設定してしまえばかなり完結にテストをかける強力なツールです。
Mockソリューションを試す。 EasyMock と jMock の比較
ここまでで EasyMock と jMock の簡単な使い方を見てきました。
まだまだ使いこなしてはいないのですが、ここまでの知識をまとめておきたいと思います。
まず、Mock と Stub の違いについて。
EasyMock とか jMock に触れる前は、Mock と Stub が自分の中で明確に区別できていませんでした。
Wikipediaによると両者は異なるもので、Mock は Stub と比較して正しく利用されているかを検証する機能が付加されているとの事です。
確かに今まで Stub と認識していたものは、「呼ばれたときに値を返す」部分を代用する様なものでした。
しかし EasyMock や jMock はメソッドが想定されたパラメータ、順序で呼ばれるかを検証することができました。
この点で Stub とは 区別される物のようです。Mock が Stub の一種と言うことですね。
さて、 EasyMock、jMock ともに機能に大きな違いはなさそうです。
- interface に対する Mockの作成
- concrete class、abstract class に対する Mockの作成
- メソッドが呼ばれているかを検査
- メソッドが想定するパラメータで呼ばれているかを検査
- メソッドが想定する順番で呼ばれているかを検査
- メソッドの戻り値を偽造
また、基本的な使い方の比較をコードでしてみます。
まず、Mock を作る部分。
//EasyMock mock = createMock(IService.class); // jMock context = new Mockery(); mock = context.mock(IService.class);次に想定される結果を検査する部分。
EasyMockの場合。
// モックオブジェクトに対して想定される呼び出しを記述します。 mock.echo();
// 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。 replay(mock); sample.execute(); verify(mock);jMockの場合
// モックオブジェクトに対して想定される呼び出しを記述します。
context.checking(new Expectations() {
{oneOf(mock).echo();
}});
// 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。 sample.execute(); context.assertIsSatisfied();EasyMockは想定される呼び出しを記述し、replay〜verifyメソッドで囲われた部分を検査します。
jMockは想定される呼び出しを context.checkingメソッド内のExpectationsクラス初期化子に記述し、context.checking〜context.assertIsSatisfiedメソッドで囲われた部分を検査します。
個人的には jMock の書き方の方が決められた場所が明確で好きなのですが、好みの問題かもしれません。
他にも "Mock" という文字列を含むソリューションがありましたが、試すまでには至りませんでした。
まず rMock なのですが、対応していないのかもしれないのですが TestNGにからの利用方法がわからなかったので、試しませんでした。
ただ、ドキュメントを読む限り BDD的な要素を絡めているようでおもしろそうではあります。
あと、ClassMock と呼ばれるものもありましたが、こちらは Mock と呼ばれるには必要な、正しく呼ばれているかを検証する機能がなく、簡単にクラスを作るための仕組みを提供している様です。
Mockとしての機能は他のソリューションに委ねている様で、ドキュメントにも jMock と連携した場合の例が載せられています。
Mock の使いどころとして浮かぶのは Servlet とかのテストなのではないでしょうか。
つまり擬似的な実装クラスを作るのが面倒な部分です。
Wicket だと WicketFilter クラス何かは Mock を使ったテストなんかは有効なのではと思います。
Mockソリューションを試す。 EasyMock + classextension
jMockではインターフェース以外のクラスについてモックオブジェクトを作れましたが、EasyMockについてもEasyMock Class Extension を導入することで実現できます。
まずEasyMockのサイトからライブラリをダウンロードするなり、Mavenを使っているならpom.xmlを以下のように編集します。
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymockclassextension</artifactId>
<version>2.4</version>
</dependency>
以前、インターフェースに対してモックオブジェクトを作成する部分はこんな感じでした。
// IServiceクラスのモックを作成します。 mock = createMock(IService.class);EasyMock Class Extensionを導入する場合は、classextensionパッケージ下にある上記と同名のメソッドを使用するようにします。
具体的には、org.easymock.EasyMock.createMock から org.easymock.classextension.EasyMock.createMockメソッドを使用するようにimport文を変更します。
他にも replyメソッドやverifyメソッドもclassextensionパッケージ下のものを使うことになるので、インポート文に下記を加えておくとよいでしょう。
import static org.easymock.classextension.EasyMock.*;
これでjMockと同様に、インターフェース以外のモックオブジェクトの作成が可能になります。
Mockソリューションを試す。 jMock
前回はEasyMockについて簡単なサンプルを作って見ました。
今回はjMockで同じような事を試して、EasyMockとどう違うか見ていきます。
まずjMockのサイトからライブラリをダウンロードするなり、Mavenを使っているならpom.xmlを以下のように編集します。
<dependency>
<groupId>org.jmock</groupId>
<artifactId>jmock</artifactId>
<version>2.5.1</version>
</dependency>
</dependency>
テスト対象のクラスは前回と同じです。再掲します。
package test.easymock;
public class MockSample {
private IService service;
public MockSample(IService service) {
this.service = service;
}
public void execute() {
service.echo();
}
public String getName() {
return service.get();
}
public interface IService {
public void echo();
public String get();
}
}
モックオブジェクとを作成して、テスト対象のクラスに注入して、適切に呼ばれているかをテストするという流れはEasyMockと同様です。想定される呼び出しの記述にインスタンス初期化子を使っている所が個人的には見やすく感じました。
戻り値の指定も同様にインスタンス初期化子の中に記述します。
package test.easymock;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
import test.easymock.MockSample.IService;
public class TestJMockSample {
private Mockery context;
private MockSample sample;
private IService service;
@BeforeMethod
public void setupMock() {
// IServiceクラスのモックを作成します。
context = new Mockery();
service = context.mock(IService.class);
// MockSampleのコンストラクタの引数としてmockオブジェクトを渡します。
sample = new MockSample(service);
}
@Test public void callEcho() {
// モックオブジェクトに対して想定される呼び出しを記述します。
context.checking(new Expectations() {
{oneOf(service).echo();
}});
// 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
sample.execute();
context.assertIsSatisfied();
}
@Test public void callGet() {
// モックオブジェクトに対して想定される呼び出しと戻り値を記述します。
context.checking(new Expectations() {
{oneOf(service).get();
will(returnValue("hoge"));
}});
// 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
// また、戻り値が想定された値かをチェックしています。
assertEquals(sample.getName(), "hoge");
context.assertIsSatisfied();
}
}
ドキュメントを読む限りEasyMockと機能的にはそれほど大差ないのではないかと思ったのですが、jMockではインターフェースのないクラスもテストすることができます。jMockでは内部的に使用しているProxyクラスに関して、reflectionを使う場合とcglibを使う場合に切り替ることができます。
cglibを使うことで、インターフェースのないクラスのモックオブジェクトを作成出きるようになっています。
インターフェースのないクラスのモックオブジェクトを作る場合は、pom.xmlに下記を追加します。
<dependency> <groupId>org.jmock</groupId> <artifactId>jmock-legacy</artifactId> <version>2.5.1</version> </dependency>インターフェースのないクラスをテストするときは、Mockeryクラスを生成するときに、インスタンス初期化で下記の様に指定します。
context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
この様に、jMockはEasyMockと比べるとインスタンス初期化子で記述する点や、インターフェースがなくてもモックオブジェクトを作成できる点で、優れている所も多そうです。
Mockソリューションを試す。 EasyMock
ユニットテストをやってると、クラスの依存関係によってテストクラスの作成が難しいことがよくあります。
今の現場は特にユニットテストを作成する事が多いので、この障害にぶち当たることが頻繁です。
こういったケースではモックオブジェクトを作成したりするのですが、Javaではモックオブジェクトを作るためのライブラリーがいろいろあるようです。
ざっと見た限り、jMock, rMock, djUnit(Virtual Mock) EasyMock等が上げられます。
最終的にはこれらの違いを考察したいのですが、今回はEasyMockを試してみます。
まずEasyMockのサイトからライブラリをダウンロードするなり、Mavenを使っているならpom.xmlを以下のように編集します。
<dependency> <groupId>org.easymock</groupId> <artifactId>easymock</artifactId> <version>2.4</version> </dependency>
テスト対象のクラスは以下のようなものを用意しました。
コンストラクタでIServiceクラスをインターフェースとするクラスを受けとります。
このクラスのメソッドを実行するだけのメソッドexecute()、getName()があります。
public class MockSample {
private IService service;
public MockSample(IService service) {
this.service = service;
}
public void execute() {
service.echo();
}
public String getName() {
return service.get();
}
}
interface IService {
public void echo();
public String get();
}
モックオブジェクトの使用法としては、まずモックオブジェクトを作成する処理を記述します。createMock(IService.class);のようにしてモックオブジェクトを作成します。この部分はGenericsを使っているため、キャストもなくすっきりしていますね。
次に、生成したモックオブジェクトを何らかの方法でテスト対象のクラスに注入して、適切に呼ばれているかをテストしていきます。
想定される呼び出しを記述し、replay(mock)〜verify(mock)の間で想定通りに呼び出されているかをテストしていきます。
また、モックオブジェクトが呼び出された時の戻り値も指定することができます。
package test.easymock;
import static org.easymock.EasyMock.*;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
public class TestEasyMockSample {
private IService mock;
private MockSample sample;
@BeforeMethod
public void setupMock() {
// IServiceクラスのモックを作成します。
mock = createMock(IService.class);
// MockSampleのコンストラクタの引数としてmockオブジェクトを渡します。
sample = new MockSample(mock);
}
@Test public void callEcho() {
// モックオブジェクトに対して想定される呼び出しを記述します。
mock.echo();
// 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
replay(mock);
sample.execute();
verify(mock);
}
@Test public void callGet() {
// モックオブジェクトに対して想定される呼び出しと戻り値を記述します。
expect(mock.get()).andReturn("hoge");
// 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
// また、戻り値が想定された値かをチェックしています。
replay(mock);
String str = sample.getName();
assertEquals(str, "hoge");
verify(mock);
}
}
このように、モックオブジェクトは、それ自身が正しく呼ばれているか検証することができます。
ただ、内部的にはjava.lang.reflectパッケージのProxyクラスを用いていることから、インターフェースの存在が必須になっているようです。
この点がEasyMockの弱点かなと思います。
また、テスト対象のクラスにモックオブジェクトを登録する必要があります。
うまく登録できる入り口があればよいのですが、この点がテストクラスの作成を困難にすることが多々あります。
この点はモックに限らないことですが。
次はjMockを見ていく予定です。
TestNGでマルチスレッドテストを試してみた
この前m2eclipseを使ってみたのも、TestNGを試してみるためでした。
TestNGのマルチスレッドなテストを試してみたかったんです。
ちなみにJunitはずっと使ってるけど、3.x系列しか使った事がなかったので、アノテーションベースのテストクラスも初体験でした。
アノテーションに関してはJunit4.x系列なら扱えるんですけどね。
さてさて、マルチスレッドなテストを試すため、下記のようなスレッドアンセーフティーな実装をしてみました。
package test;
public class UnSafety {
private static StringBuffer sb;
public static String unsync() {
sb = new StringBuffer();
sb.append(“松”);
sb.append(“竹”);
sb.append(“梅”);
return sb.toString();
}
}
TestNGでマルチスレッドなテストをするためには、アノテーションのパラメータにスレッドの数(threadPoolSize)と実行回数(invocationCount)を指定します。下記の例ではスレッド数3で、実行回数を10回としています。
package test;
import org.testng.annotations.Test;
import static org.testng.Assert.;
public class TestUnSafe {
@Test(threadPoolSize=3, invocationCount=10)
public void testUnSafety() {
assertEquals(UnSafety.unsync(), “松竹梅”);
}
}
}
実行結果はこんな感じ。ばっちりテストできてるみたいです。PASSED: testUnSafety PASSED: testUnSafety PASSED: testUnSafety PASSED: testUnSafety PASSED: testUnSafety PASSED: testUnSafety PASSED: testUnSafety FAILED: testUnSafety java.lang.AssertionError: expected:<松竹梅> but was:<松竹梅梅梅> at test.TestUnSafe.testUnSafety(TestUnSafe.java:9) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675) at java.lang.Thread.run(Thread.java:595) ... Removed 16 stack frames FAILED: testUnSafety java.lang.AssertionError: expected:<松竹梅> but was:<松竹梅梅> at test.TestUnSafe.testUnSafety(TestUnSafe.java:9) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675) at java.lang.Thread.run(Thread.java:595) ... Removed 16 stack frames FAILED: testUnSafety java.lang.AssertionError: expected:<松竹梅> but was:<松竹梅梅> at test.TestUnSafe.testUnSafety(TestUnSafe.java:9) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675) at java.lang.Thread.run(Thread.java:595) ... Removed 16 stack frames*マルチスレッドとは関係ないのですが、TestNGではexpected(予想値)とactual(実行値)の引数の順番が、Junitと逆なんですね。
エラーメッセージを見て戸惑いました。