Java
JUnit
Mockito
PowerMock

PowerMock+Mockito1xでのケース別モックのかけかた

概要

Mockito1xでのケース別モックのかけかた
https://qiita.com/taka_22/items/0c13aacf6ffbc2c77970
の続き。

前提

Mockito 1.10.19
PowerMock 1.7.3
JUnit 4.11
Java 1.8.0_151

ケース

依存オブジェクトをnewで生成しているケース、staticメソッド、privateメソッドを扱います。前回のパターンも踏まえて、以下の12パターンに分類しました。

No パターン モック対象メソッドの戻り型 モック範囲 モック動作
7 モックしたいクラスがnewで生成されている void以外 全体 指定した戻り値を返す
8 モックしたいクラスがnewで生成されている void以外 全体 例外を投げる
9 モックしたいクラスがnewで生成されている void 全体 例外を投げる
10 モック対象がstaticメソッド void以外 全体 指定した戻り値を返す
11 モック対象がstaticメソッド void以外 全体 例外を投げる
12 モック対象がstaticメソッド void 全体 例外を投げる
13 モック対象がprivateメソッド void以外 全体 指定した戻り値を返す
14 モック対象がprivateメソッド void以外 全体 例外を投げる
15 モック対象がprivateメソッド void 全体 例外を投げる
16 モック対象がprivateメソッド void以外 一部のメソッドのみ 指定した戻り値を返す
17 モック対象がprivateメソッド void以外 一部のメソッドのみ 例外を投げる
18 モック対象がprivateメソッド void 一部のメソッドのみ 例外を投げる

テスト用に作ったクラス

DateUtil.java

import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private Boolean flag;
public static String getCurrentDate() {
Date d = new Date();
return new SimpleDateFormat("yyyy/MM/dd").format(d);
}
public static void setCurrentDate(long currentTime) {
Date d = new Date();
d.setTime(currentTime);
}
public String getYYYYMMDDStringWrapper(Date date) throws Exception {
return getYYYYMMDDString(date);
}
private String getYYYYMMDDString(Date date) throws Exception {
return new SimpleDateFormat("yyyyMMdd").format(date);
}
private void setFlag(Boolean flag) throws Exception {
this.flag = flag;
}
}

テストの書き方

PowerMockでのテストの書き方

  1. mock/spy/doThrowといった、Mockitoで使っていたメソッドは、PowerMock専用のものを使う必要があります。
  2. @RunWithでPowerMockRunnerを指定する
  3. @PrepareForTestでモック対象テストを指定する

import static org.powermock.api.mockito.PowerMockito.doThrow;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.spy;
import static org.powermock.api.mockito.PowerMockito.whenNew;

@RunWith(PowerMockRunner.class)
@PrepareForTest({ DateUtil.class })
public class PowerMockTest {
...

No.7

whenNewでnewで生成されたオブジェクトをモックしたものを返すようにする。

/**
 * newで生成されるオブジェクト、publicメソッド、戻り型はvoid以外、全部モック、値を戻す
 */
@Test
public void test007() throws Exception {
    // 期待値は2015/05/05
    Calendar c = Calendar.getInstance();
    c.set(2015, 4, 5);
    Date expected = c.getTime();

    // 記録フェーズ
    Date dateMocked = mock(Date.class);
    whenNew(Date.class).withNoArguments().thenReturn(dateMocked);
    when(dateMocked.getTime()).thenReturn(expected.getTime());

    // リプレイフェーズ
    String strDate = DateUtil.getCurrentDate();

    // 検証フェーズ
    assertThat(strDate, is("2015/05/05"));
}

No.8

/**
 * newで生成されるオブジェクト、publicメソッド、戻り型はvoid以外、全部モック、例外を投げる
 */
@Test
public void test008() throws Exception {
    // 記録フェーズ
    Date dateMocked = mock(Date.class);
    whenNew(Date.class).withNoArguments().thenReturn(dateMocked);
    when(dateMocked.getTime()).thenThrow(new RuntimeException("aaa"));

    // リプレイ&検証フェーズ
    try {
        DateUtil.getCurrentDate();
        fail();
    } catch (RuntimeException e) {
        assertThat(e.getMessage(), is("aaa"));
    }
}

No.9

/**
 * newで生成されるオブジェクト、publicメソッド、戻り型はvoid、全部モック、例外を投げる
 */
@Test
public void test009() throws Exception {
    // 記録フェーズ
    Date dateMocked = mock(Date.class);
    whenNew(Date.class).withNoArguments().thenReturn(dateMocked);
    doThrow(new RuntimeException("bbb")).when(dateMocked).setTime(anyLong());

    // リプレイ&検証フェーズ
    try {
        DateUtil.setCurrentDate(123456789L);
        fail();
    } catch (RuntimeException e) {
        assertThat(e.getMessage(), is("bbb"));
    }
}

No.10

mockStaticでstaticメソッドをモックする。

/**
 * public staticメソッド、戻り型はvoid以外、全部モック、値を戻す
 */
@Test
public void test010() {
    // 記録フェーズ
    PowerMockito.mockStatic(String.class);
    PowerMockito.when(String.valueOf(anyBoolean())).thenReturn("false");

    // リプレイ&検証フェーズ
    assertThat(String.valueOf(true), is("false"));
    assertThat(String.valueOf(false), is("false"));
}

No.11

/**
 * public staticメソッド、戻り型はvoid以外、全部モック、例外を投げる
 */
@Test
public void test011() {
    // 記録フェーズ
    PowerMockito.mockStatic(String.class);
    PowerMockito.when(String.valueOf(true)).thenThrow(new RuntimeException("aaa"));

    // リプレイ&検証フェーズ
    try {
        String.valueOf(true);
        fail();
    } catch (RuntimeException e) {
        assertThat(e.getMessage(), is("aaa"));
    }
    assertNull(String.valueOf(false));
}

No.12

/**
 * public staticメソッド、戻り型はvoid、全部モック、例外を投げる
 */
@Test
public void test012() {
    // 記録フェーズ
    PowerMockito.mockStatic(DateUtil.class);
    try {
        doThrow(new RuntimeException("bbb")).when(DateUtil.class, "setCurrentDate", anyLong());
    } catch (Exception e) {
        fail();
    }

    // リプレイ&検証フェーズ
    try {
        DateUtil.setCurrentDate(12345L);
        fail();
    } catch (RuntimeException e) {
        assertThat(e.getMessage(), is("bbb"));
    }
}

No.13

全体をモックしてしまうので、モックしたメソッドは直接リフレクションで呼び出す。

/**
 * privateメソッド、戻り型はvoid以外、全部モック、値を戻す
 */
@Test
public void test013() throws Exception {
    // 記録フェーズ
    DateUtil dateMocked = mock(DateUtil.class);
    PowerMockito.when(dateMocked, "getYYYYMMDDString", (Date) any()).thenReturn("12345678");

    // リプレイフェーズ
    Method getYYYYMMDDStringMethod = DateUtil.class.getDeclaredMethod("getYYYYMMDDString", Date.class);
    getYYYYMMDDStringMethod.setAccessible(true);
    String actual = (String)getYYYYMMDDStringMethod.invoke(dateMocked, new Date());

    // 検証フェーズ
    assertThat(actual, is("12345678"));
}

No.14

/**
 * privateメソッド、戻り型はvoid以外、全部モック、例外を投げる
 */
@Test
public void test014() throws Exception {
    // 記録フェーズ
    DateUtil dateMocked = mock(DateUtil.class);
    PowerMockito.when(dateMocked, "getYYYYMMDDString", (Date) any()).thenThrow(new Exception("aaa"));

    // リプレイ&検証フェーズ
    try {
        Method getYYYYMMDDStringMethod = DateUtil.class.getDeclaredMethod("getYYYYMMDDString", Date.class);
        getYYYYMMDDStringMethod.setAccessible(true);
        getYYYYMMDDStringMethod.invoke(dateMocked, new Date());
        fail();
    } catch (Exception e) {
        if (e instanceof InvocationTargetException) {
            InvocationTargetException ite = (InvocationTargetException) e;
            assertThat(ite.getTargetException().getMessage(), is("aaa"));
        } else {
            fail();
        }
    }
}

No.15

/**
 * privateメソッド、戻り型はvoid、全部モック、例外を投げる
 */
@Test
public void test015() throws Exception {
    // 記録フェーズ
    DateUtil dateMocked = mock(DateUtil.class);
    PowerMockito.doThrow(new Exception("bbb")).when(dateMocked, "setFlag", anyBoolean());

    // リプレイ&検証フェーズ
    Method setFlagMethod = DateUtil.class.getDeclaredMethod("setFlag", Boolean.class);
    setFlagMethod.setAccessible(true);
    try {
        setFlagMethod.invoke(dateMocked, true);
        fail();
    } catch (Exception e) {
        if (e instanceof InvocationTargetException) {
            InvocationTargetException ite = (InvocationTargetException) e;
            assertThat(ite.getTargetException().getMessage(), is("bbb"));
        } else {
            fail();
        }
    }
}

No.16

モックのかけ方は、spyを使うのが違うだけ。あとはNo.13-15と同じ。
全部をモックしないので、publicメソッド経由でテストすることが可能。もちろん、リフレクションでも可能。
/**
* privateメソッド、戻り型はvoid以外、一部モック、値を戻す
*/
@Test
public void test016() throws Exception {
// 記録フェーズ
DateUtil dateMocked = spy(new DateUtil());
PowerMockito.doReturn("12345678").when(dateMocked, "getYYYYMMDDString", (Date) any());

    // リプレイフェーズ
    String strDate = dateMocked.getYYYYMMDDStringWrapper(new Date());

    // 検証フェーズ
    assertThat(strDate, is("12345678"));
}

No.17

/**
 * privateメソッド、戻り型はvoid以外、一部モック、例外を投げる
 */
@Test
public void test017() throws Exception {
    // 記録フェーズ
    DateUtil dateMocked = spy(new DateUtil());
    PowerMockito.doThrow(new RuntimeException("aaa")).when(dateMocked, "getYYYYMMDDString", (Date) any());

    // リプレイ&検証フェーズ
    try {
        dateMocked.getYYYYMMDDStringWrapper(new Date());
        fail();
    } catch (RuntimeException e) {
        assertThat(e.getMessage(), is("aaa"));
    }
}

No.18

/**
 * privateメソッド、戻り型はvoid、一部モック、例外を投げる
 */
@Test
public void test018() throws Exception {
    // 記録フェーズ
    DateUtil dateMocked = spy(new DateUtil());
    PowerMockito.doThrow(new Exception("bbb")).when(dateMocked, "setFlag", anyBoolean());

    // リプレイ&検証フェーズ
    Method setFlagMethod = DateUtil.class.getDeclaredMethod("setFlag", Boolean.class);
    setFlagMethod.setAccessible(true);
    try {
        setFlagMethod.invoke(dateMocked, true);
        fail();
    } catch (Exception e) {
        if (e instanceof InvocationTargetException) {
            InvocationTargetException ite = (InvocationTargetException) e;
            assertThat(ite.getTargetException().getMessage(), is("bbb"));
        } else {
            fail();
        }
    }
}

参考

Mockito

http://site.mockito.org/

PowerMock

https://github.com/powermock/powermock

動作確認したプロジェクト

https://github.com/taka2/mockito-sample

Mockito1xでのケース別モックのかけかた

https://qiita.com/taka_22/items/0c13aacf6ffbc2c77970