JUnit

JUnitのPowerMockを利用してprivateメソッドのMock化と呼出確認をしてみた

privateメソッドの場合も、staticメソッドの場合と同様に、PowerMockを利用してMock化することができる。今回は、privateメソッドのMock化と呼出確認を行ってみたので、そのサンプルプログラムを共有する。

前提条件

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

JUnitのPowerMockを利用してstaticメソッドのMock化と呼出確認をしてみたこれまでは、Mockitoを利用してインスタンスメソッドをMock化する方法について記載していたが、今回はstaticメソッドのMock...

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

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

DateCheckUtil.javaの内容は以下の通り。そのうち、今回のテストプログラムに関連するのは「checkDate」「isCorrectDate」メソッドとなる。

package com.example.demo;

import java.time.LocalDate;
import java.time.chrono.JapaneseChronology;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.util.Locale;

public class DateCheckUtil {

    /**
     * 日付チェック処理を行う
     * @param year 年
     * @param month 月
     * @param day 日
     * @return 判定結果(1:年が空、2:月が空、3:日が空、4:年月日が不正、5:年月日が未来日、0:正常
     */
    public static int checkDate(String year, String month, String day){
        final String dateFormat = "uuuuMMdd";
        if(isEmpty(year)){
            return 1;
        }
        if(isEmpty(month)){
            return 2;
        }
        if(isEmpty(day)){
            return 3;
        }
        String dateStr = year + addZero(month) + addZero(day);
        if(!isCorrectDate(dateStr, dateFormat)){
            return 4;
        }
        if(isFutureDate(dateStr, dateFormat)){
            return 5;
        }
        return 0;
    }

    /**
     * DateTimeFormatterを利用して日付チェックを行う
     * @param dateStr チェック対象文字列
     * @param dateFormat 日付フォーマット
     * @return 日付チェック結果
     */
    private static boolean isCorrectDate(String dateStr, String dateFormat){
        if(isEmpty(dateStr) || isEmpty(dateFormat)){
            return false;
        }
        //日付と時刻を厳密に解決するスタイルで、DateTimeFormatterオブジェクトを作成
        DateTimeFormatter df = DateTimeFormatter.ofPattern(dateFormat)
                .withResolverStyle(ResolverStyle.STRICT);
        try{
            //チェック対象文字列をLocalDate型の日付に変換できれば、チェックOKとする
            LocalDate.parse(dateStr, df);
            return true;
        }catch(Exception e){
            return false;
        }
    }

    /**
     * 日付の文字列が未来日かどうかを判定する
     * @param dateStr チェック対象文字列
     * @param dateFormat 日付フォーマット
     * @return 判定結果
     */
    private static boolean isFutureDate(String dateStr, String dateFormat){
        if(!isCorrectDate(dateStr, dateFormat)){
            return false;
        }
        LocalDate dateStrDate = convertStrToLocalDate(dateStr, dateFormat);
        LocalDate now = LocalDate.now();
        if(dateStrDate.isAfter(now)){
            return true;
        }
        return false;
    }

    /**
     * 日付の文字列を日付型に変換した結果を返す
     * @param dateStr 日付の文字列
     * @param dateFormat 日付のフォーマット
     * @return 変換後の文字列
     */
    private static LocalDate convertStrToLocalDate(String dateStr, String dateFormat){
        if(isEmpty(dateStr) || isEmpty(dateFormat)){
            return null;
        }
        //日付と時刻を厳密に解決するスタイルで、暦体系は和暦体系で、
        //DateTimeFormatterオブジェクトを作成
        DateTimeFormatter df = DateTimeFormatter.ofPattern(dateFormat, Locale.JAPAN)
                .withChronology(JapaneseChronology.INSTANCE)
                .withResolverStyle(ResolverStyle.STRICT);
        //日付の文字列をLocalDate型に変換して返却
        return LocalDate.parse(dateStr, df);
    }

    /**
     * 数値文字列が1桁の場合、頭に0を付けて返す
     * @param intNum 数値文字列
     * @return 変換後数値文字列
     */
    private static String addZero(String intNum){
        if(isEmpty(intNum)){
            return intNum;
        }
        if(intNum.length() == 1){
            return "0" + intNum;
        }
        return intNum;
    }

    /**
     * 引数の文字列がnull、空文字かどうかを判定する
     * @param str チェック対象文字列
     * @return 文字列チェック結果
     */
    public static boolean isEmpty(String str){
        if(str == null || "".equals(str)){
            return true;
        }
        return false;
    }
}
「FlexClip」はテンプレートとして利用できる動画・画像・音楽などが充実した動画編集ツールだったテンプレートとして利用できるテキスト・動画・画像・音楽など(いずれも著作権フリー)が充実している動画編集ツールの一つに、「FlexCli...

また、DateCheckUtilTest.javaの内容は以下の通り。

package com.example.demo;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.junit.Assert.assertEquals;

@RunWith(PowerMockRunner.class)
@PrepareForTest({DateCheckUtil.class})
public class DateCheckUtilTest {

    /**
     * 前処理(各テストケースを実行する前に行われる処理)
     */
    @Before
    public void init(){
        //DateCheckUtilクラスをSpyで一部をMock設定
        PowerMockito.spy(DateCheckUtil.class);
    }

    /**
     * DateCheckUtilクラスのcheckDateメソッドの確認
     * (DateCheckUtil.isCorrectDateをMock設定した場合の確認)
     */
    @Test
    public void testDateCheckUtilCheckDateMock() throws Exception{
        //privateメソッドであるDateCheckUtil.isCorrectDateを呼び出したときに、
        //falseを返却するようMock設定
        PowerMockito.doReturn(false)
                .when(DateCheckUtil.class, "isCorrectDate"
                        , Mockito.any(), Mockito.any());

        //DateCheckUtil.checkDateを呼び出し、戻り値を確認
        //checkDateメソッドの引数に正しい年月日を指定しているが、
        //isCorrectDateメソッドでfalseを返すようにMock設定したため、
        //DateCheckUtil.checkDateメソッドの戻り値が4になる
        int retVal = DateCheckUtil.checkDate("2012", "12", "27");
        assertEquals(4, retVal);
    }

    /**
     * DateCheckUtilクラスのcheckDateメソッドの確認
     * (DateCheckUtil.isCorrectDateの呼出回数と引数の確認)
     */
    @Test
    public void testDateCheckUtilCheckDateVerify() throws Exception{
        //DateCheckUtil.checkDateを呼び出し、戻り値を確認
        int retVal = DateCheckUtil.checkDate("2012", "12", "32");
        assertEquals(4, retVal);

        //DateCheckUtil.isCorrectDateメソッドの呼出回数が1回であることを確認し、
        //その際の引数も取得
        ArgumentCaptor<String> dateStrArg = ArgumentCaptor.forClass(String.class);
        ArgumentCaptor<String> dateFormatArg = ArgumentCaptor.forClass(String.class);
        PowerMockito.verifyPrivate(DateCheckUtil.class, Mockito.times(1))
                .invoke("isCorrectDate", dateStrArg.capture(), dateFormatArg.capture());

        //DateCheckUtil.isCorrectDateメソッドの引数を確認
        assertEquals("20121232", dateStrArg.getValue());
        assertEquals("uuuuMMdd", dateFormatArg.getValue());
    }

}

上記プログラムのtestDateCheckUtilCheckDateMockメソッドでは、DateCheckUtil.javaのprivateメソッド「isCorrectDate」をMock化している。PowerMockitoのwhen句で、staticメソッドの場合と同じように、第一引数にstaticメソッドを含むクラス名.classを、第二引数にメソッド名を、第三引数以降にメソッドの引数(任意値はMockito.any()で指定)を指定している。

また、testDateCheckUtilCheckDateVerifyメソッドでは、DateCheckUtil.javaのprivateメソッド「isCorrectDate」の呼出回数と引数を取得している。PowerMockito.verifyPrivateメソッドを利用し、呼出回数はMockito.times句で確認し、invokeメソッド内でメソッド名と引数の指定を行っている。さらに、staticメソッドの場合と同様に、そのメソッド呼出時の引数は、ArgumentCaptorクラスを利用することで取得できる。

その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/junit-powermock-private/demo



フリーランスエンジニアのエージェントは就業中でも無料で登録できるITエンジニアには、フリーランスという働き方がある。 フリーランスとは、会社や団体などに所属せず、仕事に応じて自由に契約する人のこ...

今回作成した「DateCheckUtilTest.java」の実行結果は以下の通り。
JUnitプログラムの実行結果

要点まとめ

  • privateメソッドの場合も、PowerMockを利用することで、Mock化や呼出回数・引数の確認が行える。