JUnit

JUnitのPowerMockを利用してstaticメソッドの例外処理のテストをしてみた

staticメソッドについても、PowerMockを利用すれば、例外を発生させるJUnitプログラムを作成できる。今回は、PowerMockを利用して、下記「前提条件」とサンプルプログラムを作成してみたので、共有する。

前提条件

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

JUnitのMockitoで例外処理のテストをしてみたMockitoを利用すると、例外を発生させるMockプログラムも作成することができる。 今回は例外処理を含むプログラムをテストするJU...

やってみたこと

  1. テスト対象プログラムの作成と実行
  2. JUnitのプログラムの作成と実行

 

テスト対象プログラムの作成と実行

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

テスト対象のコントローラクラスの内容は以下の通り。

package com.example.demo;

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

import java.io.FileNotFoundException;

@Controller
public class DemoController {

    @RequestMapping("/")
    public String index(Model model){
        String textString;
        try{
            textString = DemoUtil.getTextString();
        }catch (Exception e){
            String errMsg;
            if(e instanceof FileNotFoundException){
                errMsg = "ファイルが見つかりませんでした";
            }else{
                errMsg = "入出力例外が発生しました";
            }
            //エラー時はerror.htmlに遷移
            model.addAttribute("errMsg", errMsg);
            return "error";
        }
        //正常時はindex.htmlに遷移
        model.addAttribute("textString", textString);
        return "index";
    }
}



また、上記プログラムから呼び出されるユーティリティクラスの内容は以下の通りで、staticメソッドになっている。

package com.example.demo;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class DemoUtil {

    public static String getTextString() throws IOException{
        StringBuilder sb = new StringBuilder();
        try(BufferedReader br = new BufferedReader(
                new FileReader("C:\\tmp\\test.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
        }catch (FileNotFoundException ex1){
            throw ex1;
        }catch (IOException ex2){
            throw ex2;
        }
        return sb.toString();
    }

}

さらに、本プログラムを実行した結果は、前提条件の記事の「テスト対象プログラムの実行」に記載した通りとなる。



JUnitのプログラムの作成と実行

build.gradleの内容は以下の通り。PowerMockが利用できるための設定を追加している。

plugins {
	id 'org.springframework.boot' version '2.1.7.RELEASE'
	id 'java'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	//PowerMockが利用できるための設定
	testCompile 'org.powermock:powermock-module-junit4:2.0.0-RC.4'
	testCompile 'org.powermock:powermock-api-mockito2:2.0.0-RC.4'
}

また、テスト対象のコントローラクラス「DemoController.java」から呼ばれるユーティリティクラス「DemoUtil.java」で例外を発生させた場合の、JUnitのサンプルプログラムの内容は以下の通り。

package com.example.demo;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.ui.Model;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Map;

//staticメソッドをMock化するにはPowerMockを利用
//@PrepareForTestアノテーションで、staticメソッドを含むクラスを指定
@RunWith(PowerMockRunner.class)
@PrepareForTest({DemoUtil.class})
public class DemoControllerTest {

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

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

        //DemoUtilクラスをMock化
        PowerMockito.mockStatic(DemoUtil.class);
    }

    /**
     * DemoControllerクラスのindexメソッドの確認
     */
    @Test
    public void testDemoControllerNormal() throws Exception {
        //Mockオブジェクト呼出時の戻り値を設定(DemoUtil.java)
        //when句に(staticメソッドをもつ)クラス名・staticメソッド名・引数を順に
        //指定するが例外が発生し得るため、「throws Exception」を付与する
        PowerMockito.doReturn("abcdeテスト")
                .when(DemoUtil.class, "getTextString");

        //modelオブジェクトを取得し、テスト対象クラスのメソッドを実行
        Model model = DemoControllerTestUtil.getModel();
        String returnVal = demoController.index(model);

        //戻り値が"index"であることを確認
        assertEquals("index", returnVal);

        //modelオブジェクトの設定値を確認
        //demoComponent.getTextStringが呼ばれた場合は設定したMockの戻り値が設定され、
        //エラーメッセージが設定されないことを確認
        Map<String, Object> modelValue = model.asMap();
        assertEquals("abcdeテスト", modelValue.get("textString"));
        assertNull(modelValue.get("errMsg"));
    }

    @Test
    public void testDemoControllerNotFoundException() throws Exception {
        //Mockオブジェクト呼出時の例外を設定(DemoUtil.java)
        //doThrow句内に例外を設定するが、発生し得ない例外(例:Exception)は設定できない
        PowerMockito.doThrow(new FileNotFoundException())
                .when(DemoUtil.class, "getTextString");

        //modelオブジェクトを取得し、テスト対象クラスのメソッドを実行
        Model model = DemoControllerTestUtil.getModel();
        String returnVal = demoController.index(model);

        //戻り値が"error"であることを確認
        assertEquals("error", returnVal);

        //modelオブジェクトの設定値を確認
        //FileNotFoundExceptionが発生した場合のエラーメッセージ
        //「ファイルが見つかりませんでした」が設定され、
        //textStringが設定されないことを確認
        Map<String, Object> modelValue = model.asMap();
        assertEquals("ファイルが見つかりませんでした", modelValue.get("errMsg"));
        assertNull(modelValue.get("textString"));
    }

    @Test
    public void testDemoControllerIOException() throws Exception {
        //Mockオブジェクト呼出時の例外を設定(DemoUtil.java)
        //doThrow句内に例外を設定するが、発生し得ない例外(例:Exception)は設定できない
        PowerMockito.doThrow(new IOException())
                .when(DemoUtil.class, "getTextString");

        //modelオブジェクトを取得し、テスト対象クラスのメソッドを実行
        Model model = DemoControllerTestUtil.getModel();
        String returnVal = demoController.index(model);

        //戻り値が"error"であることを確認
        assertEquals("error", returnVal);

        //modelオブジェクトの設定値を確認
        //IOExceptionが発生した場合のエラーメッセージ「入出力例外が発生しました」が
        //設定され、textStringが設定されないことを確認
        Map<String, Object> modelValue = model.asMap();
        assertEquals("入出力例外が発生しました", modelValue.get("errMsg"));
        assertNull(modelValue.get("textString"));
    }
}

上記の「testDemoControllerNotFoundException」「testDemoControllerIOException」メソッドのように、PowerMockito.doThrow句を利用することで、指定したクラスの指定したstaticメソッドを実行した際に、指定した例外がスローされるようにMock設定できる。

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

さらに、上記プログラムを実行した結果は以下の通り。
JUnitテストクラスの実行結果

要点まとめ

  • PowerMockito.doThrow句を利用することで、指定したクラスの指定したstaticメソッドを実行した際に、指定した例外がスローされるようにMock設定できる。