JUnit

JunitのMockitoを利用してモック化したオブジェクトの引数と呼出回数を取得してみた

JUnitのテストを行う際、Mock化したメソッドの戻り値がvoid型で、Mock化したメソッドが呼ばれたかどうかわからない場合がある。このような場合に、Mock化したメソッドの呼出回数や引数を取得することで、Mock化したメソッドの呼出確認が行える。

今回は、テーブル更新を行う箇所をMock化したメソッドの呼出回数や引数を取得してみたので、そのサンプルプログラムを共有する。

前提条件

下記記事の実装が完了していること。

JUnitでMockitoを利用して処理の一部をモック化してみたJUnitのテストを行う際、処理の一部をMock化してテストしたい場合がある。例えば、SpringBootを利用したプログラムで、データ...

サンプルプログラムの内容

作成したサンプルプログラムの構成は以下の通り。
サンプルプログラムの構成
なお、上記の赤枠のうち、「DemoServiceImplTest2.java」が今回新規で作成したプログラムとなる。

ウズウズカレッジJavaコースはわかりやすい動画教材と充実した就業サポートで優良企業を目指せるプログラミングスクールだったJavaは、世界中で広く使われていて、現在の需要が高く将来性もある開発言語になります。 https://www.acrovision....

「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;
    }
}
freelance hubを利用して10万件を超える案件情報からJava Spring案件を検索してみたfreelance hubは、レバテックフリーランスやフリエン(furien)を始めとした多くのフリーランスエージェントの案件をまとめて...

さらに、今回作成した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メソッドを実行することで、設定された引数のリストが取得できる。