テストコードを作成する際のTipsをまとめておこうと思う。
テストコードはしっかり組むべきではあるが、たまにカバレッジを達成するためだけに
テストコードを書く必要のあるタイミングがあるので
それ用にアプローチや作成方法などをまとめた。
テストクラスの対象で、別クラスをnewしてインスタンス作っている場合
案1:インスタンス作成を該当クラスに移す。
// setterでインスタンスを作成するように変える
private クラス 変数名
public void setInstance() {
    this.変数名 = new クラス()
}
public クラス getInstance() {
    return this.変数名
}
案2: mockConstructionを利用して、newInstanceのモックを作る
下記のようにmockConstructionを利用して、new Instanceの部分をモックにすることで一応可能。
一部だけモックにしたい場合は、withSettings().defaultAnswer(CALLS_REAL_METHODS)を利用して、実際の処理を動かすようにしよう。
//
try (MockedConstruction<クラス> mockedClass =
        Mockito.mockConstruction(クラス.class, 
        (mock, 
        //下に記述したスタブ以外のメソッドをモックではなく実態で処理させるための設定
        withSettings().defaultAnswer(CALLS_REAL_METHODS)
        context) -> {
          //スタブにしたいメソッド(羅列可能)
          Mockito.doReturn(適当な戻り値).when(mock).メソッド();
          Mockito.doReturn(適当な戻り値).when(mock).メソッド();
          ....
        }); // 1-1
    ) {
        //テストを実行
    }
もしdefaultAnswerを利用する場合は、以下の記事で描かれてある通りスタブの記述の仕方に注意
案3:@Inject を利用して依存性を注入する
本来あるべき姿はおそらくこれだと思う。
こうすることで、テストのときに依存するクラスのインスタンスを無理矢理コントロールさせるなどが必要なくなるはず。
staticのメソッドを処理する場合
mockStaticを使う
//
try (MockedStatic<クラス> mockedClass =
        Mockito.mockStatic(クラス.class)
    ) {
        //staticメソッドのスタブを作る
        mockedClass.when(
            () -> クラス.staticメソッド()).thenReturn(戻り値)
        //テストを実行
    }
finalクラス
そもそもFinalクラスをモック化する必要がある実装が良くない…という話も多分あると思う。
案1:ラップするクラスを作る
おそらくこれが最善の策かと思われる?
案2:mockConstructionを使う
同じくmockConstructionを使えば、理屈上可能ではあるが
このFinalクラスになっているものは色んなソースで呼ばれていると思われるので、別のクラスで異常終了してしまう可能性がある。
private, protectedのメソッド、フィールド変数をテストする場合
privateなフィールド変数に依存しているクラスはそこそこあると思うので
フィールド変数を設定するテストコードは割と書くかもしれない
リフレクションを使う
フィールド変数を設定する場合
//テストしたいクラスのインスタンス作成
テストしたいクラス test = new テストしたいクラス();
// private変数のフィールドを取得
Field field = test.getClass().getDeclaredField("privateなフィールド変数名");
// private変数へのアクセス制限を解除
field.setAccessible(true);
// 値を書き換え
field.set(target, 設定したい値);
フィールド変数を取得する場合
getterがあるなら、getterを利用するべき。
ないなら極力入れたほうがよい。
一応やるなら下記でできるはず
//テストしたいクラスのインスタンス作成
テストしたいクラス test = new テストしたいクラス();
// private変数を受け取りたい場合
Field field = test.getClass().getDeclaredField("privateなフィールド変数名");
// private変数へのアクセス制限を解除
field.setAccessible(true);
// 値を取得
field.get(test);
private, protectedメソッドを呼び出す場合
以下で実行し、テストを実行はできる。
が、そもそもprivateメソッドをテストしなければいけないとなるとソースのリファクタリングを考えるべき。
おそらく、メソッド内の処理がごっちゃになっているか、うまく分割ができていない。
テスト対象クラス test = new テスト対象クラス();
Method method = テスト対象クラス.class.getDeclaredMethod("<メソッド名>", 引数の型1, 引数の型2...);
method.setAccessible(true);
int actual = (戻り値の型)method.invoke(<インスタンス>,引数1,引数2...);
voidメソッドに対しての検証
基本的にvoidと言っても、privateフィールド変数への設定や、ログ出力などの処理をしているはず。
おおよそ以下のことをテストすることになるはずだ。
- メソッドの呼び出し回数をチェックする
- フィールド変数に設定するなら、その設定が予想通りかを検証する
- ログに出力するメッセージをチェックする
できないテストはもちろんある
例えば、実際にHTTPリクエストを送信してどんな結果を返しても大丈夫か?みたいなテストをするなら
実態を知らないと想定値や実際のリクエストを書くことはできない
また、それらは環境が影響し予想外の結果が返ってくる可能性もある。
そうなるとさすがにテストコードではなく、実際にリクエストを渡した方がいい。
カバレッジを全部で〇〇%や、100%担保するとかなっているなら
モックして適当なテストを書いて、実際のテストもやる…みたいなことをする必要があると思われる。
