処理途中でnewしているクラスをMock化するには、PowerMockitoのwhenNewメソッドを利用する必要がある。また、PowerMockを利用すれば、MockMakerファイルが無くても、finalメソッドをMock化できる。
今回は、PowerMockを利用して処理途中でnewしているクラスをMock化するサンプルプログラムを作成してみたので、共有する。
前提条件
下記記事の実装が完了していること。
作成したサンプルプログラムの内容と実行結果
作成したサンプルプログラムの構成は以下の通り。
なお、上記の赤枠は、前提条件のプログラムから変更したプログラムである。
build.gradleの内容は以下の通りで、PowerMockを利用するための設定を追加している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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' } |
また、コントローラクラスの内容は以下の通りで、DemoComponentクラスのインスタンスを生成する処理を、DemoControllerクラスのコンストラクタに移動している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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 { /** * DemoComponentクラス * (インスタンス生成をコンストラクタで行うため、アノテーションは付与していない) */ DemoComponent demoComponent; /** * コンストラクタ */ public DemoController(){ demoComponent = new DemoComponent(); } /** * 初期表示画面に遷移する * @param model Modelオブジェクト * @return 初期表示画面へのパス */ @RequestMapping("/") public String index(Model model){ String nowDateTime = demoComponent.getNowDateTime(); String nowDateTimeFinal = demoComponent.getNowDateTimeFinal(); model.addAttribute("nowDateTime", nowDateTime); model.addAttribute("nowDateTimeFinal", nowDateTimeFinal); return "index"; } } |
さらに、DemoComponentクラスの内容は以下の通りで、アノテーション「@Component」を削除している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package com.example.demo; import java.time.LocalDateTime; public class DemoComponent { /** * 現在日時を取得し返却する * @return 現在日時 */ public String getNowDateTime(){ LocalDateTime nowDateTime = LocalDateTime.now(); return nowDateTime.toString(); } /** * finalメソッドで現在日時を取得し返却する * @return 現在日時 */ public final String getNowDateTimeFinal(){ LocalDateTime nowDateTime = LocalDateTime.now(); return nowDateTime.toString(); } } |
上記ソース内容で、 Spring Bootアプリケーションを起動し、「http:// (ホスト名):(ポート番号)」とアクセスすると、以下の画面が表示される。この実行結果は、前提条件の記事と同じ内容である。
また、コントローラクラスのテストを行うプログラムの内容は以下の通りで、initメソッド内で、テスト対象クラスで呼ばれるクラスとそのインスタンス生成処理をMock化している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | package com.example.demo; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; 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.time.LocalDateTime; import java.util.Map; import static org.junit.Assert.assertEquals; //demoComponentクラスのインスタンス生成処理をMock化するためPowerMockを利用し、 //@PrepareForTestアノテーションで、テスト対象クラスとMockクラスを指定 @RunWith(PowerMockRunner.class) @PrepareForTest({DemoController.class, DemoComponent.class}) public class DemoControllerTest { /** * テスト対象のクラス * (インスタンス生成をinitメソッドで実行するため、アノテーションは付与していない) */ private DemoController demoController; /** * テスト対象のクラス内で呼ばれるクラスのMockオブジェクト * (インスタンス生成をinitメソッドで実行するため、アノテーションは付与していない) */ private DemoComponent demoComponent; /** * 前処理(各テストケースを実行する前に行われる処理) */ @Before public void init() throws Exception { //テスト対象クラスで呼ばれるクラスとそのインスタンス生成処理をMock化 //finalメソッドをMock化できるようにするには、PowerMockを利用する必要がある demoComponent = PowerMockito.mock(DemoComponent.class); PowerMockito.whenNew(DemoComponent.class).withNoArguments() .thenReturn(demoComponent); //テスト対象クラスのインスタンスを生成 demoController = new DemoController(); //Mockの設定 Mockito.when(demoComponent.getNowDateTime()) .thenReturn(getNowDateTimeStr()); Mockito.when(demoComponent.getNowDateTimeFinal()) .thenReturn(getNowDateTimeStr()); } /** * DemoControllerクラスのindexメソッドを確認 */ @Test public void testDemoController(){ //Modelオブジェクトを生成 Model model = DemoControllerTestUtil.getModel(); //テスト対象クラスのメソッドを実行 String strPath = demoController.index(model); //テスト対象クラスのメソッドで設定されたMapオブジェクトを表示 System.out.println(); System.out.println("*** コントローラクラスのindexメソッドで設定されたMapオブジェクト ***"); System.out.println(model.asMap()); System.out.println(); //テスト対象クラスのメソッドの実行結果を確認 assertEquals("index", strPath); Map<String, Object> mapObj = model.asMap(); assertEquals("2020-07-14T20:54:12", mapObj.get("nowDateTime")); assertEquals("2020-07-14T20:54:12", mapObj.get("nowDateTimeFinal")); } /** * 現在時刻を生成し文字列化して返却 * @return 現在時刻の文字列 */ private String getNowDateTimeStr(){ LocalDateTime nowDateTime = LocalDateTime.of( 2020, 7, 14, 20, 54, 12); return nowDateTime.toString(); } } |
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/junit-powermock-new-final/demo
DemoControllerTestクラスを実行した結果は以下の通りで、finalメソッドであるgetNowDateTimeFinalメソッドも、finalメソッドでないgetNowDateTimeメソッドも、Mock化により返却された値が設定されることが確認できる。
要点まとめ
- 処理途中でnewしているクラスをMock化するには、Mock化するクラスをPowerMockでMock化し、whenNewメソッドでインスタンス化を行えばよい。
- PowerMockを利用すれば、MockMakerファイルが無くても、finalメソッドをMock化できる。