JUnitのテストを行う際、Mock化したメソッドの戻り値がvoid型で、Mock化したメソッドが呼ばれたかどうかわからない場合がある。このような場合に、Mock化したメソッドの呼出回数や引数を取得することで、Mock化したメソッドの呼出確認が行える。
今回は、テーブル更新を行う箇所をMock化したメソッドの呼出回数や引数を取得してみたので、そのサンプルプログラムを共有する。
前提条件
下記記事の実装が完了していること。
サンプルプログラムの内容
作成したサンプルプログラムの構成は以下の通り。
なお、上記の赤枠のうち、「DemoServiceImplTest2.java」が今回新規で作成したプログラムとなる。
「DemoServiceImpl.java」は、前提条件の記事と変更していない。下記ソースでは、今回テスト対象とするcreateOrUpdateメソッドに関する部分のみを記載している。
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」の内容は以下の通り。
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メソッドを実行することで、設定された引数のリストが取得できる。