質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.01%

  • Java

    12156questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • Spring Boot

    378questions

    Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

  • JUnit

    162questions

    JUnitは、Javaで開発されたプログラムのユニットテストを行うためのアプリケーションフレームワークです。簡単にプログラムのユニットテストを自動化することができ、結果もわかりやすく表示されるため効率的に開発時間を短縮できます。

Mockitoを使用したprivateメソッドのテストを実施したい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,346

gu3.net

score 3

前提・実現したいこと

Mockitoを使用して、privateメソッドの単体テストを作成しています。
privateメソッド内でインスタンスをnewして、メソッド呼び出しをしている箇所をMock化し、
自分で生成したオブジェクトをそのまま返すようなテストを実行したいと考えています。

下記のように実装しましたが、NullPointerになっています。
CommandクラスをMock化しているので、sampleA内のCmd.executeは実行されず、
when句で設定したResultが返却されると思っているのですが、どうやら実行されており、Processbuilder.startでエラーとなっています。

発生している問題・エラーメッセージ

Caused by: java.lang.NullPointerException
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1012)

該当のソースコード

@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleATest {

    private static final SampleA sampleA = new SampleA();

    @Test
    public void testExecuteCommand() throws Exception {
        Method method = SampleA.class.getDeclaredMethod("executeCommand", Object.class, String.class);
        method.setAccessible(true);

        Result result = mock(Result.class);
        Whitebox.setInternalState(result, "exitValue", 0);

        Command command = spy(Command.class);
        when(command.execute(anyObject(), anyObject())).thenReturn(result);

        Result result2 = (Result) method.invoke(sampleA, obj, strs);
        assertThat(result2.getExitValue(), is(0));
    }
}
public class SampleA {
    private void executeCommand(Object Obj, String str) {
        Command Cmd = new Command();
        Result result = Cmd.execute(obj, str);
        if (result.getExitValue() != 0) {
            throw new Exception("error" + result.getExitValue());
        }
    }
}
public class Command {
    public Result execute(Object Obj, String param) {
        Result result = new Result();

        List<String> CmdList = new ArrayList<>();
        CmdList.add(obj.command);
        CmdList.add(param);

        ProcessBuilder builder = new ProcessBuilder(CmdList);
        Process process = builder.start();
        try {
            result.exitValue = process.exitValue();
        } finally {
            process.destroy();
        }

        return result;
    }
}

補足情報(言語/FW/ツール等のバージョンなど)

  • SpringBoot 1.5.x
  • JUnit 4.x
  • Mockito 2.x

コードにマスクをしておりますので、不整合がありましたら申し訳ありません。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

気になる点がいくつかあります。

  • assertしている内容
    Result result2 = (Result) method.invoke(sampleA, obj, strs); のところ、メソッドに返り値があるように書かれていますが、ここで呼ばれる「executeCommand」メソッドはvoidです。
    SampleA内でresultがどこにも保存されないため、この場合、Exceptionがなく終了すること、くらいしか確認できないかもしれません(省略しただけならすみません)。

  • CommandクラスがSampleAのメソッド内でnewされている。
    一般に、メソッド内でnewしてオブジェクトを作るようなソースは、依存が生まれテストがしづらいとされています。⇒参考
    こうしてしまうとモック化は少し複雑化します。おそらくMockitoだけでは出来なさそうです。
    以下のようにしてなんとかなりました。

pom.xml

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <scope>test</scope>
</dependency>

テストケース

@RunWith(PowerMockRunner.class)
// @SpringBootTest
public class SampleATest {

    // private static final SampleA sampleA = new SampleA(); <- モック化が必要

    @Test
    public void testExecuteCommand() throws Exception {
      SampleA sampleA = mock(SampleA.class);
// ~中略~
      Result result = new Result();
      result.setExitValue(0); // <- ここは想像です
      when(command.execute(any(Object.class), anyString())).thenReturn(result);
      whenNew(Command.class).withNoArguments().thenReturn(command); // ※1


※1 -> これをするのにPowerMockが必要です。Commandクラスをnewしたときにcommand変数を返す としています。

1.assertしたい内容、2.実装内での依存(new)の排除、2つの観点でソースを見てみてください。
ご不明点ありましたら、追記をお願いします。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 91.01%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Java

    12156questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • Spring Boot

    378questions

    Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

  • JUnit

    162questions

    JUnitは、Javaで開発されたプログラムのユニットテストを行うためのアプリケーションフレームワークです。簡単にプログラムのユニットテストを自動化することができ、結果もわかりやすく表示されるため効率的に開発時間を短縮できます。