今回は、JUnitのPowerMockのうち、Mockitoの@Spyに相当するものを利用して、staticメソッドのMock化をしてみたので、サンプルプログラムを共有する。
前提条件
下記記事の実装が完了していること。
やってみたこと
テスト対象プログラムの作成と実行
作成したサンプルプログラムの構成は以下の通り。
なお、上図の赤枠は、前提条件のプログラムから変更した内容である。
テスト対象のコントローラクラスの内容は以下の通り。
package com.example.demo; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class DemoController { @RequestMapping("/") public String index(Model model){ String realString1 = DemoUtil.getRealString1(); String realString2 = DemoUtil.getRealString2(); DemoUtil.testVoid(); model.addAttribute("realString1", realString1); model.addAttribute("realString2", realString2); return "index"; } }
また、上記プログラムから呼び出されるユーティリティクラスの内容は以下の通りで、全てstaticメソッドになっている。
package com.example.demo; public class DemoUtil { public static String getRealString1(){ return "realString1"; } public static String getRealString2(){ return "realString2"; } public static void testVoid(){ System.out.println("testVoid"); } }
また、Spring Bootアプリケーションを起動し、「http:// (ホスト名):(ポート番号)」とアクセスすると、以下の画面が表示される。
JUnitのプログラムの作成と実行
build.gradleの内容は以下の通り。PowerMockが利用できるための設定を追加している。
plugins { id 'org.springframework.boot' version '2.1.7.RELEASE' id 'java' } apply plugin: 'io.spring.dependency-management' group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' //PowerMockが利用できるための設定 testCompile 'org.powermock:powermock-module-junit4:2.0.0-RC.4' testCompile 'org.powermock:powermock-api-mockito2:2.0.0-RC.4' }
また、テスト対象のコントローラクラス「DemoController.java」から呼ばれるユーティリティクラス「DemoUtil.java」を、PowerMockito.spyを利用して一部をMock化してみたJUnitのサンプルプログラムの内容は以下の通り。
package com.example.demo; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import static org.junit.Assert.assertEquals; import org.mockito.MockitoAnnotations; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.springframework.ui.Model; import java.util.Map; //staticメソッドをMock化するにはPowerMockを利用 //@PrepareForTestアノテーションで、staticメソッドを含むクラスを指定 @RunWith(PowerMockRunner.class) @PrepareForTest({DemoUtil.class}) public class DemoControllerTest1 { /** * テスト対象クラス */ @InjectMocks private DemoController demoController; /** * 前処理(各テストケースを実行する前に行われる処理) */ @Before public void init() { //@Mockアノテーションのモックオブジェクトを初期化 //これを実行しないと@Mockアノテーション、@InjectMocksを付与した //Mockオブジェクトが利用できない MockitoAnnotations.initMocks(this); //DemoUtilクラスをSpyで一部をMock設定 PowerMockito.spy(DemoUtil.class); //Mockオブジェクト呼出時の値を設定 //DemoUtil.getRealString1が呼ばれた場合のみMock設定し、 //DemoUtil.getRealString2が呼ばれた場合はMock設定しない PowerMockito.doReturn("mockString1").when(DemoUtil.class); DemoUtil.getRealString1(); //voidメソッドが呼ばれた場合は何もしないようにするには、下記のMock設定が必要 PowerMockito.doNothing().when(DemoUtil.class); DemoUtil.testVoid(); } /** * DemoControllerクラスのindexメソッドの確認 */ @Test public void testDemoController(){ //modelオブジェクトを取得し、テスト対象クラスのメソッドを実行 Model model = DemoControllerTestUtil.getModel(); String returnVal = demoController.index(model); //戻り値が"index"であることを確認 assertEquals("index", returnVal); //modelオブジェクトの設定値を確認 //DemoUtil.getRealString1が呼ばれた場合は設定したMockの戻り値が設定され、 //DemoUtil.getRealString2が呼ばれた場合は実際の戻り値が設定される Map<String, Object> modelValue = model.asMap(); assertEquals("mockString1", modelValue.get("realString1")); assertEquals("realString2", modelValue.get("realString2")); //modelオブジェクトの設定値を出力 System.out.println("realString1の値 : " + modelValue.get("realString1") + ", realString2の値 : " + modelValue.get("realString2")); } }
上記プログラムによって、DemoUtil.getRealString1()を呼び出した際はMock化した値「mockString1」が取得でき、DemoUtil.getRealString2()を呼び出した際は実際の値「realString2」が取得できる。また、void型のDemoUtil.testVoid()を呼び出した際は何もしない設定となる。
さらに、同等の処理をPowerMockito.mockStaticを利用してMock化してみたサンプルプログラムの内容は以下の通り。
package com.example.demo; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.MockitoAnnotations; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.springframework.ui.Model; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; //staticメソッドをMock化するにはPowerMockを利用 //@PrepareForTestアノテーションで、staticメソッドを含むクラスを指定 @RunWith(PowerMockRunner.class) @PrepareForTest({DemoUtil.class}) public class DemoControllerTest2 { /** * テスト対象クラス */ @InjectMocks private DemoController demoController; /** * 前処理(各テストケースを実行する前に行われる処理) */ @Before public void init() { //@Mockアノテーションのモックオブジェクトを初期化 //これを実行しないと@Mockアノテーション、@InjectMocksを付与した //Mockオブジェクトが利用できない MockitoAnnotations.initMocks(this); //DemoUtilクラスのメソッドを全てMock設定 PowerMockito.mockStatic(DemoUtil.class); //Mockオブジェクト呼出時の値を設定 //DemoUtil.getRealString1が呼ばれた場合のみMock設定し、 //DemoUtil.getRealString2が呼ばれた場合はMock設定しない PowerMockito.doReturn("mockString1").when(DemoUtil.class); DemoUtil.getRealString1(); } /** * DemoControllerクラスのindexメソッドの確認 */ @Test public void testDemoController(){ //modelオブジェクトを取得し、テスト対象クラスのメソッドを実行 Model model = DemoControllerTestUtil.getModel(); String returnVal = demoController.index(model); //戻り値が"index"であることを確認 assertEquals("index", returnVal); //modelオブジェクトの設定値を確認 //DemoUtil.getRealString1が呼ばれた場合は設定したMockの戻り値が設定され、 //DemoUtil.getRealString2が呼ばれた場合はnullが設定される Map<String, Object> modelValue = model.asMap(); assertEquals("mockString1", modelValue.get("realString1")); assertNull(modelValue.get("realString2")); //modelオブジェクトの設定値を出力 System.out.println("realString1の値 : " + modelValue.get("realString1") + ", realString2の値 : " + modelValue.get("realString2")); } }
上記プログラムによって、DemoUtil.getRealString1()を呼び出した際はMock化した値「mockString1」が取得でき、DemoUtil.getRealString2()を呼び出した際はnullが取得される。また、void型のDemoUtil.testVoid()を呼び出した際の設定は特に記載していないが、何もしない設定となる。
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/junit-powermock-spy/demo
要点まとめ
- PowerMockito.spyを利用すると、staticメソッドを含むクラスの一部をMock化できる。