[JUnit]newクラス、private・staticメソッドをモック化する

PowerMockitoを使って、Mockitoではできなかったnewしたクラスのモック化、privateメソッドのモック化、staticメソッドのモック化ができるようになりました。

今回はこの3つ全てのテスト方法を紹介していきます。

今回はJUnit4で検証しています。

[スポンサーリンク]

タイトルにあるテスト対象メソッド内で・・・と言葉で表現しても今ひとつピンときませんよね。。

実際のコードをみてもらえばすぐに分かると思いますのでコチラのをみてください。


フォーカスが当たっている部分が、テスト対象以外のメソッドを指しています。

ようはフォーカスがあたっていない部分のみをJUnitでテストするためには、フォーカス部分をモックに置き換える必要があります。

なので、フォーカスされているstaticメソッドをモック化するという時に今回紹介する方法を使っていきます。


こちらのようにテスト対象のメソッドが、newしているクラスのメソッドを呼んでいる場合でもモック化することは可能です。

もくじ

テスト対象やCallしているメソッドの種類によってモック化する方法が異なります。
Newしたクラスから別クラスのpublicメソッドをCallする場合
Newしたクラスから自クラスのprivateメソッドをCallする場合
NewしたクラスからStaticメソッドをCallする場合
StaticクラスからStaticメソッドをCallする場合
Staticクラスから自クラスのprivateメソッドする場合

Newしたクラスから別クラスのpublicメソッドをCallする場合

まず最初は、newしたクラスからnewしたクラスのメソッドをモック化するパターンです。

イメージとしては、new > new public のケースです。

こちらがテスト対象となるクラス・メソッドです。
4行目で別クラスのメソッドを呼んでいます。この部分をモック化していきましょう。

public class BookLogic {
    public String getTitle() {
        BookDao dao = new BookDao();
        return dao.findTitle();
    }
}

こちらは上記クラスから呼び出されている(モック化される)メソッドです。

public class BookDao {
    public String findTitle() {
        return "コンビニ人間";
    }
}

こちらがJUnitクラスになります。
BookDaoのfindTitleメソッドをモックにしています。

@RunWith(PowerMockRunner.class)
@PrepareForTest(BookLogic.class)
public class NewMockTest {

    /**
     * newするクラス内で呼んでいるメソッドをモック化する方法、引数なし
     */
    @Test
    public void noArg() throws Exception {
        BookLogic logic = new BookLogic();
        BookDao bookDaoMock = PowerMockito.mock(BookDao.class);
        PowerMockito.when(bookDaoMock.findTitle()).thenReturn("test");
        PowerMockito.whenNew(BookDao.class).withNoArguments().thenReturn(bookDaoMock);
   
        String result = logic.getTitle();
        
        assertThat(result, is("test"));
    }
}

findTitleメソッドでは、「コンビニ人間」という文字列を返しているのですが、モック化したものでは「test」を返すようにしています。

引数ありの場合

先程のメソッドは引数なしのメソッドでしたが、引数ありのパターンのJUnitも紹介します。

こちらがテスト対象のクラスになります。
先程のクラスとの違いは、4行目で呼び出しているメソッドが引数ありとなっています。

public class BookLogic {
    public String getTitle() {
        BookDao dao = new BookDao();
        return dao.findTitle("name");
    }
}

こちらは呼び出されている(モック化される)メソッドです。
引数ありですね。

public class BookDao {
    public String findTitle(String name) {
        return "サラバ!" + name;
    }
}

こちらがJUnitクラスです。
引数ありの場合は、withAnyArgumentsを使ってメソッドをモック化していきます。

@RunWith(PowerMockRunner.class)
@PrepareForTest(BookLogic.class)
public class NewMockTest {

    /**
     * newするクラス内で呼んでいるメソッドをモック化する方法、引数あり
     */
    @Test
    public void arg() throws Exception {
        BookLogic logic = new BookLogic();
        BookDao bookDao = PowerMockito.mock(BookDao.class);
        PowerMockito.when(bookDao.findTitle("name")).thenReturn("test");
        PowerMockito.whenNew(BookDao.class).withAnyArguments().thenReturn(bookDao);
   
        String result = logic.getTitle();
        
        assertThat(result, is("test"));
    }
}

また、12行目でモック化するメソッドに対して引数を指定する必要があります。この部分の引数は実際に渡される引数と同じ値にしていきます。(引数の値を抽象化することもできますが、ここではわかりやすさを優先して同じ値を指定します。)

Newしたクラスから自クラスのprivateメソッドをCallする場合

先程は他クラスのメソッドをモック化する方法でしたが、今回は自クラスのprivateメソッドをモック化する方法です。

イメージとしては、new > new private のケースです。

4行目でprivateメソッドを呼び出しています。このprivateメソッドをモック化していきます。

public class MusicLogic {

    public String getMusicTitle() {
        return getTitle() + " ウルフルズ";
    }
    private String getTitle() {
        return "ガッツだぜ";
    }
}

こちらが先程のクラスのJUnitクラスになります。
privateのgetTitleメソッドをモックにしています。

@RunWith(PowerMockRunner.class)
@PrepareForTest(MusicLogic.class)
public class NewPrivateMockTest {

    /**
     * newするクラス内で呼んでいるprivateメソッドをモック化する方法、引数なし
     */
    @Test
    public void noArg() throws Exception {
        MusicLogic cdLogic = PowerMockito.spy(new MusicLogic());
        PowerMockito.when(cdLogic, "getTitle").thenReturn("test");

        String result = cdLogic.getMusicTitle();
        
        assertThat(result, is("test ウルフルズ"));
    }
}

privateメソッドをモック化するには、PowerMockitoのspyメソッドを使ってモック化していきます。

引数ありの場合

さて、privateメソッドが引数を持っている場合はどうでしょうか?
ご安心を!
privateメソッドが引数を持っていてもモック化することは可能です。

こちらがテスト対象のクラスになります。
4行目で呼び出しているpriavateメソッドに引数を渡しています。

public class MusicLogic {

    public String getMusicTitle() {
        return getTitle("名もなき詩") + " Mr Children";
    }
    private String getTitle(String name) {
        return name;
    }
}

こちらがJUnitクラスです。
引数ありの場合は、whenメソッドの第三引数に実際に渡している引数を指定します。

@RunWith(PowerMockRunner.class)
@PrepareForTest(MusicLogic.class)
public class NewPrivateMockTest {

    /**
     * newするクラス内で呼んでいるprivateメソッドをモック化する方法、引数あり
     */
    @Test
    public void arg() throws Exception {
        MusicLogic cdLogic = PowerMockito.spy(new MusicLogic());
        PowerMockito.when(cdLogic, "getTitle", "名もなき詩").thenReturn("test");

        String result = cdLogic.getMusicTitle();
        
        assertThat(result, is("test Mr Children"));
    }
}

では引数が2つの場合、3つの場合と増えていったらどうすればいいのでしょうか?
その場合は第4引数、第5引数と増やして指定してやって下さい。

NewしたクラスからStaticメソッドをCallする場合

さて、続いてはStaticメソッドをモック化する方法です。

イメージとしては、new > public static のケースです。

こちらが今回JUnitでテスト対象となるクラスです。
4行目でstaticメソッドを呼び出していますね。この部分をモック化していきます。

public class DoramaLogic {

    public String getTitle() {
        return DoramaUtil.get() + " 放送予定!";
    }
}

こちらは呼び出されている(モック化される)staticメソッドです。

public class DoramaUtil {

    public static String get() {
        return "王様のレストラン";
    }
}

こちらが先程のクラスのJUnitクラスになります。
PowerMockito.mockStaticを使って、staticメソッドをモック化しています。

@RunWith(PowerMockRunner.class)
@PrepareForTest(DoramaUtil.class)
public class NewStaticTest {

    /**
     * newするクラス内で呼んでいるstaticメソッドをモック化する方法、引数なし
     */
    @Test
    public void noArg() throws Exception {
        DoramaLogic logic = new DoramaLogic();
        
        PowerMockito.mockStatic(DoramaUtil.class);
        PowerMockito.when(DoramaUtil.get()).thenReturn("テストドラマ");

        String result = logic.getTitle();
        
        assertThat(result, is("テストドラマ 放送予定!"));
    }
}

ハイライト部分がstaticメソッドをモック化する方法です。

引数ありの場合

staticメソッドも引数ありのパターンも見ていきましょう。

こちらがテスト対象のクラスになります。
4行目で呼び出しているpriavateメソッドに引数を渡していますね。

public class DoramaLogic {

    public String getTitle() {
        return DoramaUtil.get("踊る大捜査線") + " 放送予定!";
    }
}

こちらは呼び出されている(モック化される)staticメソッドです。引数がありますね。

public class DoramaUtil {

    public static String get(String name) {
        return name + "?";
    }
}

こちらがJUnitクラスです。
引数ありの場合は、whenメソッドの第三引数に実際に渡している引数を指定します。

@RunWith(PowerMockRunner.class)
@PrepareForTest(DoramaUtil.class)
public class NewStaticTest {

    /**
     * newするクラス内で呼んでいるstaticメソッドをモック化する方法、引数あり
     */
    @Test
    public void arg() throws Exception {
        DoramaLogic logic = new DoramaLogic();
        
        PowerMockito.mockStatic(DoramaUtil.class);
        PowerMockito.when(DoramaUtil.get("踊る大捜査線")).thenReturn("テストドラマ");

        String result = logic.getTitle();
        
        assertThat(result, is("テストドラマ 放送予定!"));
    }
}

こちらも引数が2つの場合、3つの場合と増えていったら、第4引数、第5引数と増やして指定してやって下さい。

StaticクラスからStaticメソッドをCallする場合

では続きましてstaticメソッドからstaticメソッドを呼んでいるケースをやっていきます。

イメージとしては、public static > public static のケースです。

まずはこちらが今回JUnitでテスト対象となるクラスです。
4行目でstaticメソッドを呼び出しているこの部分をモック化していきます。

public class MovieUtil {

    public static String getTitle() {
        return ActorUtil.get() + " 主演! 羊たちの沈黙";
    }
}

こちらは呼び出されている(モック化される)staticメソッドです。

public class ActorUtil {

    public static String get() {
        return "ジョディ・フォスター";
    }
}

こちらが上記のクラスのJUnitクラスになります。
PowerMockitoのmockStaticwhenを使って、staticメソッドをモック化しています。

@RunWith(PowerMockRunner.class)
@PrepareForTest(ActorUtil.class)
public class StaticPublicTest {

    /**
     * staticクラス内で呼んでいるstaticメソッドをモック化する方法、引数なし
     */
    @Test
    public void noArg() throws Exception {
        PowerMockito.mockStatic(ActorUtil.class);
        PowerMockito.when(ActorUtil.get()).thenReturn("テストユーザ");

        String result = MovieUtil.getTitle();

        assertThat(result, is("テストユーザ 主演! 羊たちの沈黙"));
    }
}

こちらのロジックを見てもうらうとわかるように、staticメソッドからstaticメソッドを呼び出しているケースは、newクラスからstaticメソッドを呼び出しているケースのJUnitに非常ににていますね。

ちょっとややこしくなってきましたね、、、
こんな感じのJUnitは似ているということです。
static > static
new > static

引数ありの場合

static > staticの引数ありのパターンも見ていきましょう。

こちらがテスト対象のクラスになります。
4行目で呼び出しているpriavateメソッドに引数を渡していますね。
ちなみにトム・ハンクスは羊たちの沈黙にでていません。だんだんサンプルクラスが雑になってきていますね。。

public class MovieUtil {

    public static String getTitle() {
        return ActorUtil.get("トム・ハンクス") + " 主演! 羊たちの沈黙";
    }
}

こちらは呼び出されている(モック化される)staticメソッドです。引数がありますね。

public class ActorUtil {

    public static String get(String name) {
        return name + "?";
    }
}

こちらがJUnitクラスです。
引数ありの場合は、whenメソッドの第三引数に実際に渡している引数を指定します。

@RunWith(PowerMockRunner.class)
@PrepareForTest(ActorUtil.class)
public class StaticPublicTest {

    /**
     * staticクラス内で呼んでいるstaticメソッドをモック化する方法、引数あり
     */
    @Test
    public void arg() throws Exception {
        PowerMockito.mockStatic(ActorUtil.class);
        PowerMockito.when(ActorUtil.get("トム・ハンクス")).thenReturn("テストユーザ");

        String result = MovieUtil.getTitle();

        assertThat(result, is("テストユーザ 主演! 羊たちの沈黙"));
    }
}

こちらも引数が2つの場合、3つの場合と増えていったら、第4引数、第5引数と増やして指定してやって下さい。

Staticクラスから自クラスのprivateメソッドする場合

では最後にstaticメソッドから自クラスのprivateメソッドを呼んでいるケースをやっていきましょう。

イメージとしては、public static > private static のケースです。

まずはこちらが今回JUnitでテスト対象となるクラスです。
4行目でprivate staticメソッドを呼び出しているこの部分をモック化していきます。

public class ActorUtil {

    public static int getAge() {
        return age();
    }
    private static int age() {
        return 20;
    }
}

こちらが上記のクラスのJUnitクラスになります。
PowerMockitoのspywhenを使って、privateなstaticメソッドをモック化しています。

@RunWith(PowerMockRunner.class)
@PrepareForTest(ActorUtil.class)
public class StaticPrivateTest {

    /**
     * staticクラス内で呼んでいるprivateメソッドをモック化する方法、引数なし
     */
    @Test
    public void noArg() throws Exception {
        PowerMockito.spy(ActorUtil.class);
        PowerMockito.when(ActorUtil.class, "age").thenReturn(30);

        int result = ActorUtil.getAge();

        assertThat(result, is(30));
    }
}

こちらもnew > staticのJUnitに非常に似ていますね。

引数ありの場合

private メソッドに引数があるケースも見ていきましょう。

こちらがテスト対象のクラスになります。
4行目で呼び出しているpriavateメソッドに引数を渡していますね。

public class ActorUtil {

    public static int getAge(int age) {
        return age(age);
    }
    private static int age(int age) {
        return 20 + age;
    }
}

こちらがJUnitクラスです。
引数ありの場合は、whenメソッドの第三引数に実際に渡している引数を指定します。

@RunWith(PowerMockRunner.class)
@PrepareForTest(ActorUtil.class)
public class StaticPrivateTest {

    /**
     * staticクラス内で呼んでいるprivateメソッドをモック化する方法、引数あり
     */
    @Test
    public void arg() throws Exception {
        PowerMockito.spy(ActorUtil.class);
        PowerMockito.when(ActorUtil.class, "age", 50).thenReturn(100);

        int result = ActorUtil.getAge(50);

        assertThat(result, is(100));
    }
}

こちらも引数が2つの場合、3つの場合と増えていったら、第4引数、第5引数と増やして指定してやって下さい。

PowerMockitoのdependency

pom.xmlへこちらのdependencyを追加してPowerMockitoが使えるようにしましょう。

<!-- https://mvnrepository.com/artifact/org.powermock/powermock-module-junit4 -->
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>1.6.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.powermock/powermock-api-mockito -->
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito</artifactId>
    <version>1.6.6</version>
</dependency>

Javaのバージョン

今回検証したjavaのバージョンは8です。
JUnitはバージョン4です。

さいごに

JavaのJUnit用のモックライブラリはたくさんでていますが、PowerMockitoは他ライブラリに比べると本当にパワフルですね。モック化できないケースがないんじゃないのかってくらいパワフルです。

PowerMockitoを使うとテスト対象の範囲を絞ってテストすることができるので、コードもだいぶスッキリしますよ。

ぜひとも使ってみて下さい。

それでは!