CDIを使ったプログラムの単体テスト(3)

CDIを使った単体テストについての3回目はDeltaSpike Test-ControlモジュールのMockフレームワークの使い方について紹介します。前回のブログでは@ExcludeとProjectStageを組み合わせることで開発ステージに合わせてインジェクトするBeanを切り替える方法について説明しましたが、今回紹介する方法ではもっとダイレクトにMockを使ってインジェクトの対象を切り替えます。

Mockフレームワークとは

使い方を説明するために、まずインジェクションを実行する舞台となるBeanを定義します。サンプルコードとしてMyLogicというBeanを作ります。このBeanはget()メソッドの引数の10倍の数を返すもので、内部でRepositoryのsave()メソッドを呼び出します。

package org.tanoseam.examples;

import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import java.io.Serializable;

@SessionScoped
public class MyLogic implements Serializable {
	
	public MyLogic() {}
	
	@Inject
	Repository repository;
	
	public int get(int value) {
		int result = value * 10;
		repository.save("something");
		return result;
	}
}

Repositoryは次のようなシンプルなインタフェースです。

package org.tanoseam.examples;

public interface Repository {
	public void save(Object obj);
}

そして、これを実装するRdbRepositoryというBeanを定義します。メソッドが呼び出されたことだけが確認できれば良いのでメソッドの内容はプリント文だけにしておきます。

package org.tanoseam.examples;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class RdbRepository implements Repository {
	public void save(Object dummy) {
		System.out.println("RdbRepository is called");
	}
}

このようなケースにおいてMyLogicからRdbRepositoryをインジェクトするのではなく、 テスト時にはその代わりにMockのオブジェクトをインジェクトするようにします。

DeltaSpikeのCdiTestRunnerでMockを扱うにはDynamicMockMangerを使います。これには手でMockオブジェクトを定義する方法とMockフレームワークを使う方法があります。

手でMockオブジェクトを定義する方法

次のサンプルコードは、手でMockオブジェクトを定義する方法です。DynamicMockMangerのaddMock()メソッドを使って、既存のクラスであるRdbRepositoryの無名サブクラスを定義します。これで、RdbRepositoryのインスタンスの代わりに、この無名クラスのインスタンスがインジェクトされるようになります。

@RunWith(CdiTestRunner.class)
public class DynamicMockManagerTest {
	
	@Inject
	MyLogic mylogic;
	
	@Inject
    DynamicMockManager mockManager;

	@Test
	public void testMock() {
        mockManager.addMock(new RdbRepository() {
        	@Override
        	public void save(Object obj) {
                System.out.println("Mock of RdbRepository is called");
            }
        });
		assertEquals(100, mylogic.get(10));
    }
}

Mockの使用を有効にする

DeltaSpikeではこのMockの機能はDELTASPIKE-872のためにデフォルトでは有効になっていません。使えるようにするためには以下の2つのシステムプロパティを設定することで有効になります。

  • deltaspike.testcontrol.mock-support.allow_mocked_beans=true
  • deltaspike.testcontrol.mock-support.allow_mocked_producers=true

別の方法としては、META-INF/apache-deltaspike.propertiesに上記のプロパティを設定するのでも構いません。

それから必要なのはpom.xmlの設定ですが、このDynaimcMockMangerはDeltaSpike Test-Controlモジュールに含まれるので、前々回のブログと同じpom.xmlの設定で実行することができますのでそちらを参照してください。

テストの実行ログの抜粋は以下のようになります。

11 04, 2015 9:47:08 午後 org.jboss.weld.environment.se.WeldContainer initialize
INFO: WELD-ENV-002003: Weld SE container STATIC_INSTANCE initialized
11 04, 2015 9:47:08 午後 org.apache.deltaspike.testcontrol.api.junit.CdiTestSuiteRunner$LogRunListener testStarted
情報: [run] org.tanoseam.examples.DynamicMockManagerTest#testMock
Mock of RdbRepository is called
11 04, 2015 9:47:08 午後 org.apache.deltaspike.testcontrol.api.junit.CdiTestSuiteRunner$LogRunListener testFinished
情報: [finished] org.tanoseam.examples.DynamicMockManagerTest#testMock
11 04, 2015 9:47:08 午後 org.jboss.weld.environment.se.WeldContainer shutdown
INFO: WELD-ENV-002001: Weld SE container STATIC_INSTANCE shut down

Mockitoを使う方法

DynamicMockManagerはMockitoと組み合わせて使うこともできます。Mochitoを使うにはpom.xmlに依存ライブラリを追加してください。

		<dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>${mockito.version}</version>
            <scope>test</scope>
        </dependency>

次の例では、Mochitoを使って引数10を与えた場合に戻り値として99を返すMockオブジェクトを定義しています。

	@Test
	public void testMockito() {
		MyLogic mockedLogic = mock(MyLogic.class);
		when(mockedLogic.get(10)).thenReturn(99);
		mockManager.addMock(mockedLogic);

		assertEquals(99, mylogic.get(10));
    }

DeltaSpikeにおけるMock使用上の注意

DeltaSpikeのMockを使ったテストの方法はDELTASPIKE-872に記載されているように、DeltaSpike 1.5.1では、インターセプターを使ったBeanでは適用することができないという問題が報告されています。このような制限はあるものの、単体テストでMockを使うのは非常に有効な方法だと思いましたので、このブログで紹介することにしました。

広告

8 Responses to CDIを使ったプログラムの単体テスト(3)

  1. ak says:

    当記事を参考に「DynamicMockManager」によるMock作成を試そうとしております。

    ですが下記のようなメッセージが出力されてしまいます。
    The support for mocked CDI-Beans is disabled due to a reduced portability across different CDI-implementations. Please set ‘deltaspike.testcontrol.mock-support.allow_mocked_beans’ and/or ‘deltaspike.testcontrol.mock-support.allow_mocked_producers’ to ‘true’ (in ‘META-INF/apache-deltaspike.properties’) on your test-classpath.

    META-INF/apache-deltaspike.properties は用意し、必要な記載を行っているつもりなのですが、読込みがうまくされず、Eclipseのシステムプロパティにも記載を試してみましたが、同様の結果となります。

    他に考えられる原因をご存知でしたら、ご教授いただけると助かります。

    環境
    Eclipse 4.4
    JRE 8

    • neverbird says:

      何でしょうね。私が試した環境はMac+JDK1.8なのですが、もしかしたらOS依存の問題なのかもしれません。今週末にソースをアップしますので、試してもらえますか?

      • ak says:

        返信ありがとうございます。
        ソースUPいただけたら、是非試させていただきますので、よろしくお願いします。

      • neverbird says:

        とりあえず、ここに入れました。よろしくお願いします。
        https://www.dropbox.com/sh/5qpak25y2cujn51/AACzPjyd6tUCkZfVFZ-lPJCMa?dl=0

      • ak says:

        ソースUPありがとうございました。
        UPしていただいたものと比較した結果、こちらのプロジェクトに足りないJarがあった為
        それを補ってみたところ、Mock実装による結果が確認できました。
        ありがとうございました。 OS依存のような問題ではなかったようです。

  2. neverbird says:

    Mock実装が動いてよかったです。ちなみに、足りなかったJarを教えていただけませんか?私のブログの記述が足りなかったと思いますので、参考にさせてください。

  3. ak says:

    すみません、ブログの記述に誤りはありませんでした。
    実は今回テストを動かす際、Mavenより取得したjarをテスト対象のプロジェクトにコピーして実施したのですが、その際jarのコピーがもれてしまったようです。
    (対象は「deltaspike-core-impl-1.5.1.jar」)
    今回頂いたプロジェクトのライブラリと比較した結果判明しました。
    実行時のエラーメッセージに騙されてしまったというのは言い訳ですが、単純ミスでお手を煩わせてしまい、申し訳ございませんでした。

    • neverbird says:

      ご連絡ありがとうございます。今後も何かあれば気兼ねなくコメントしてください。よろしくお願いします。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。