JUnitのテストを行う際、Mock化したメソッドの戻り値がvoid型で、Mock化したメソッドが呼ばれたかどうかわからない場合がある。このような場合に、Mock化したメソッドの呼出回数や引数を取得することで、Mock化したメソッドの呼出確認が行える。
今回は、テーブル更新を行う箇所をMock化したメソッドの呼出回数や引数を取得してみたので、そのサンプルプログラムを共有する。
前提条件
下記記事の実装が完了していること。
サンプルプログラムの内容
作成したサンプルプログラムの構成は以下の通り。
なお、上記の赤枠のうち、「DemoServiceImplTest2.java」が今回新規で作成したプログラムとなる。
「DemoServiceImpl.java」は、前提条件の記事と変更していない。下記ソースでは、今回テスト対象とするcreateOrUpdateメソッドに関する部分のみを記載している。
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 | package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.BindingResult; import java.util.ArrayList; import java.util.Collection; import java.util.List; @Service public class DemoServiceImpl implements DemoService{ /** * ユーザーデータテーブル(user_data)へアクセスするマッパー */ @Autowired private UserDataMapper mapper; /** * {@inheritDoc} */ @Override @Transactional(readOnly = false) public void createOrUpdate(DemoForm demoForm){ //更新・追加処理を行うエンティティを生成 UserData userData = getUserData(demoForm); //追加・更新処理 if(demoForm.getId() == null){ userData.setId(mapper.findMaxId() + 1); mapper.create(userData); }else{ mapper.update(userData); } } /** * UserDataオブジェクトに引数のフォームの各値を設定する * @param demoForm DemoFormオブジェクト * @return ユーザーデータ */ private UserData getUserData(DemoForm demoForm){ UserData userData = new UserData(); if(!DateCheckUtil.isEmpty(demoForm.getId())){ userData.setId(Long.valueOf(demoForm.getId())); } userData.setName(demoForm.getName()); userData.setBirthY(Integer.valueOf(demoForm.getBirthYear())); userData.setBirthM(Integer.valueOf(demoForm.getBirthMonth())); userData.setBirthD(Integer.valueOf(demoForm.getBirthDay())); userData.setSex(demoForm.getSex()); userData.setSex_value(demoForm.getSex_value()); return userData; } } |
さらに、今回作成したJUnitのプログラム「DemoServiceImplTest2.java」の内容は以下の通り。
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | package com.example.demo; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.time.LocalDate; import java.util.List; import static org.mockito.Mockito.*; import static org.junit.Assert.assertEquals; public class DemoServiceImplTest2 { /** * テスト対象のクラス * (今回はSpring Bootを利用しないため、Serviceではなく * ServiceImplを対象クラスに指定している) */ @InjectMocks private DemoServiceImpl demoServiceImpl; /** * テスト対象のクラス内で呼ばれるクラスのMockオブジェクト */ @Mock private UserDataMapper mapper; /** * 前処理(各テストケースを実行する前に行われる処理) */ @Before public void init() { //@Mockアノテーションのモックオブジェクトを初期化 //これを実行しないと@Mockアノテーション、@InjectMocksを付与した //Mockオブジェクトが利用できない MockitoAnnotations.initMocks(this); //Mockの設定 //mapper.findMaxId()メソッドを実行した際の戻り値をここで設定 when(mapper.findMaxId()).thenReturn(2L); } /** * DemoServiceImplクラスのcreateOrUpdateForAddメソッド(追加時)の確認 */ @Test public void testCreateOrUpdateForAdd(){ //追加処理を行う場合の、テスト対象メソッドの引数を生成 DemoForm demoFormAdd = makeDemoForm(null, "テスト プリン3" , LocalDate.of(2014, 4, 20), SexEnum.MAN); //テスト対象メソッドの実行 demoServiceImpl.createOrUpdate(demoFormAdd); System.out.println("*** demoServiceImpl.createOrUpdateForAdd" + "(DemoForm(追加用))の実行結果 ***"); //テスト対象メソッドを実行した結果、mapper.findMaxId()が1回呼ばれたことを確認 verify(mapper, times(1)).findMaxId(); System.out.println("mapper.findMaxId()は1回呼ばれました"); //テスト対象メソッドを実行した結果、mapper.create(UserData)が //1回呼ばれたことを確認 ArgumentCaptor<UserData> userDataCaptor = ArgumentCaptor.forClass(UserData.class); verify(mapper, times(1)) .create(userDataCaptor.capture()); System.out.println("mapper.create(UserData)は1回呼ばれました"); //mapper.create(UserData)を呼び出した際の引数が想定通りであることを確認 List<UserData> listUserData = userDataCaptor.getAllValues(); assertEquals(1, listUserData.size()); UserData expectUserData = makeUserData(3L, "テスト プリン3" , LocalDate.of(2014, 4, 20), SexEnum.MAN); assertEquals(expectUserData.toString(), listUserData.get(0).toString()); System.out.println("mapper.create(UserData)の引数 : " + listUserData.get(0).toString()); //テスト対象メソッドを実行した結果、mapper.update(UserData)は //呼ばれないことを確認 //any()は任意の引数を表す verify(mapper, times(0)).update(any()); System.out.println("mapper.update(UserData)は呼ばれませんでした"); System.out.println(); } /** * DemoServiceImplクラスのcreateOrUpdateForAddメソッド(更新時)の確認 */ @Test public void testCreateOrUpdateForUpdate(){ //更新処理を行う場合の、テスト対象メソッドの引数を生成 DemoForm demoFormUpd = makeDemoForm(2L, "テスト プリン2" , LocalDate.of(2013, 3, 19), SexEnum.WOMAN); //テスト対象メソッドの実行 demoServiceImpl.createOrUpdate(demoFormUpd); System.out.println("*** demoServiceImpl.createOrUpdateForAdd" + "(DemoForm(更新用))の実行結果 ***"); //テスト対象メソッドを実行した結果、mapper.findMaxId()が呼ばれないことを確認 verify(mapper, times(0)).findMaxId(); System.out.println("mapper.findMaxId()は呼ばれませんでした"); //テスト対象メソッドを実行した結果、mapper.create(UserData)が //呼ばれないことを確認 verify(mapper, times(0)).create(any()); System.out.println("mapper.create(UserData)は呼ばれませんでした"); //テスト対象メソッドを実行した結果、mapper.update(UserData)が //1回呼ばれたことを確認 ArgumentCaptor<UserData> userDataCaptor = ArgumentCaptor.forClass(UserData.class); verify(mapper, times(1)) .update(userDataCaptor.capture()); System.out.println("mapper.update(UserData)は1回呼ばれました"); //mapper.update(UserData)を呼び出した際の引数が想定通りであることを確認 List<UserData> listUserData = userDataCaptor.getAllValues(); assertEquals(1, listUserData.size()); UserData expectUserData = makeUserData(2L, "テスト プリン2" , LocalDate.of(2013, 3, 19), SexEnum.WOMAN); assertEquals(expectUserData.toString(), listUserData.get(0).toString()); System.out.println("mapper.update(UserData)の引数 : " + listUserData.get(0).toString()); System.out.println(); } /** * ユーザーデータを生成する * @param id ID * @param name 名前 * @param birthDay 生年月日 * @param sexEnum 性別Enum * @return ユーザーデータ */ private UserData makeUserData(Long id, String name, LocalDate birthDay , SexEnum sexEnum){ UserData userData = new UserData(); if(id != null){ userData.setId(id); } userData.setName(name); if(birthDay != null){ userData.setBirthY(birthDay.getYear()); userData.setBirthM(birthDay.getMonthValue()); userData.setBirthD(birthDay.getDayOfMonth()); } if(sexEnum != null){ userData.setSex(sexEnum.getSex()); userData.setSex_value(sexEnum.getSex_value()); } return userData; } /** * Demoフォームオブジェクトを生成する * @param id ID * @param name 名前 * @param birthDay 生年月日 * @param sexEnum 性別Enum * @return Demoフォームオブジェクト */ private DemoForm makeDemoForm(Long id, String name, LocalDate birthDay , SexEnum sexEnum){ DemoForm demoForm = new DemoForm(); if(id != null){ demoForm.setId(String.valueOf(id)); } demoForm.setName(name); if(birthDay != null){ demoForm.setBirthYear(String.valueOf(birthDay.getYear())); demoForm.setBirthMonth(String.valueOf(birthDay.getMonthValue())); demoForm.setBirthDay(String.valueOf(birthDay.getDayOfMonth())); } if(sexEnum != null){ demoForm.setSex(sexEnum.getSex()); demoForm.setSex_value(sexEnum.getSex_value()); } return demoForm; } } |
Mock化したメソッドの呼出回数は、「verify((Mock化したオブジェクト名), times((呼出回数))).(呼出メソッド名(呼出メソッドの引数))」という処理によって確認できる。
また、Mock化したメソッドの引数は、「ArgumentCaptor(引数のクラス名)> (オブジェクト名) = ArgumentCaptor.forClass((引数のクラス名).class);」と宣言し、verifyメソッドの呼出メソッドの引数に「(オブジェクト名).capture()」を指定した後で、「(オブジェクト名).getAllValues()」を実行することで、設定された引数のリストが取得できる。
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/junit-mockito-verify/demo
さらに、今回作成したJUnitプログラムを実行した結果は以下の通りで、コンソールに、Mock化したメソッドの呼出回数と引数が表示される。
要点まとめ
- Mock化したメソッドの呼出回数は、verifyメソッドにより取得できる。
- Mock化したメソッドの引数は、ArgumentCaptorクラスのオブジェクトにより取得できる。verifyメソッド呼出時にそのcaptureメソッドを指定した後で、そのオブジェクトのgetAllValuesメソッドを実行することで、設定された引数のリストが取得できる。