JUnit

MockitoでfinalメソッドをMock化してみた

Mockitoのバージョンが2の場合、MockMakerファイルを配置すれば、finalメソッドをMock化することができる。今回は、MockMakerファイルを配置した上で、finalメソッドをMock化するサンプルプログラムを作成してみたので、共有する。

前提条件

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

IntelliJ IDEA上でGradleを使ってWeb画面のSpring Bootプロジェクトを作成してみたSpring Bootのプロジェクトを新規作成を「IntelliJ IDEA」のメニューから実施しようとしたところ、無料の「Commun...

また、以下のように、Mockitoのバージョンが2であること。
Mockitoのバージョン

作成したサンプルプログラムの内容と実行結果

作成したサンプルプログラムの構成は以下の通り。
サンプルプログラムの構成
なお、上記の赤枠は、前提条件のプログラムから変更したプログラムである。

src/mainフォルダ下の変更したプログラムの内容は以下の通りで、コントローラクラスで、finalメソッドとfinalでないメソッドの両方を呼び出している。

package com.example.demo;

import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

@Component
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();
    }
}
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class DemoController {

    /**
     * DemoComponentクラス
     */
    @Autowired
    DemoComponent 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";
    }
}
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>index page</title>
</head>
<body>
nowDateTimeの値: <span th:text="${nowDateTime}">nowDateTimeの値</span><br/><br/>
nowDateTimeFinalの値: <span th:text="${nowDateTimeFinal}">nowDateTimeFinalの値</span>
</body>
</html>

上記ソース内容で、 Spring Bootアプリケーションを起動し、「http:// (ホスト名):(ポート番号)」とアクセスすると、以下の画面が表示される。
テスト対象クラスの実行結果

また、MockMakerのファイルは以下のように、「src/test/resources/mockito-extensions」フォルダ下に、「org.mockito.plugins.MockMaker」というファイル名で作成し、そのファイルに「mock-maker-inline」と記載する必要がある。
MockMaker

サラリーマン型フリーランスSEという働き方でお金の不安を解消しよう先日、「サラリーマン型フリーランスSE」という働き方を紹介するYouTube動画を視聴しましたので、その内容をご紹介します。 「サ...

さらに、コントローラクラスのテストを行うプログラムの内容は以下の通りで、finalメソッドであるgetNowDateTimeFinalメソッドも、finalメソッドでないgetNowDateTimeメソッドも、Mock化できている。

package com.example.demo;

import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.ui.Model;

import java.time.LocalDateTime;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;

public class DemoControllerTest {

    /**
     * テスト対象のクラス
     */
    @InjectMocks
    private DemoController demoController;

    /**
     * テスト対象のクラス内で呼ばれるクラスのMockオブジェクト
     */
    @Mock
    private DemoComponent demoComponent;

    /**
     * 前処理(各テストケースを実行する前に行われる処理)
     */
    @Before
    public void init(){
        //@Mockアノテーションのモックオブジェクトを初期化
        //これを実行しないと@Mockアノテーション、@InjectMocksを付与した
        //Mockオブジェクトが利用できない
        MockitoAnnotations.initMocks(this);

        //Mockの設定
        when(demoComponent.getNowDateTime()).thenReturn(getNowDateTimeStr());
        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();
    }

}



また、Modelオブジェクトを生成し返却するプログラムの内容は以下の通りで、先ほどのDemoControllerTestクラスで呼び出している。

package com.example.demo;

import org.springframework.ui.Model;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class DemoControllerTestUtil {

    /**
     * Modelオブジェクトを生成し返却する
     * @return Modelオブジェクト
     */
    public static Model getModel(){
        return new Model() {
            private Map<String, Object> modelMap = new HashMap<>();
            @Override
            public Model addAttribute(String attributeName, Object attributeValue) {
                modelMap.put(attributeName, attributeValue);
                return null;
            }

            @Override
            public Model addAttribute(Object attributeValue) {
                return null;
            }

            @Override
            public Model addAllAttributes(Collection<?> attributeValues) {
                return null;
            }

            @Override
            public Model addAllAttributes(Map<String, ?> attributes) {
                return null;
            }

            @Override
            public Model mergeAttributes(Map<String, ?> attributes) {
                return null;
            }

            @Override
            public boolean containsAttribute(String attributeName) {
                return false;
            }

            @Override
            public Map<String, Object> asMap() {
                return modelMap;
            }
        };
    }
}

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

DemoControllerTestクラスを実行した結果は以下の通りで、finalメソッドであるgetNowDateTimeFinalメソッドも、finalメソッドでないgetNowDateTimeメソッドも、Mock化により返却された値が設定されることが確認できる。
JUnitの実行結果

要点まとめ

  • Mockitoのバージョンが2の場合、MockMakerファイルを配置すれば、finalメソッドをMock化することができる。
  • MockMakerのファイルは「src/test/resources/mockito-extensions」フォルダ下に「org.mockito.plugins.MockMaker」というファイル名で作成し、そのファイルに「mock-maker-inline」と記載すればよい。