[JUnit5][SpringBoot]ユニットテスト作成時にstaticメソッドを簡単にモック化する方法

業務においてユニットテストを書いていたとき、staticメソッドをモック化したい要件があったのですが、少し手が止まってしまったので、備忘録の意味も込めて記しておきます！

# プロダクトコード
簡単なプログラム例で見ていきます。
- Serviceクラス
- CalculationUtilクラス

## Serviceクラス
2つの引数を受け取り、足し算した結果に応じた戻り値にするプログラムです。
```java:Service.java
public class Service {

  public String calculateAndCheckFizzBuzz(int a, int b) {
    int result = CalculationUtil.plus(a, b);
    if (result % 15 == 0) {
      return "FizzBuzz";
    }
    if (result % 3 == 0) {
      return "Fizz";
    }
    if (result % 5 == 0) {
      return "Buzz";
    }
    return String.valueOf(result);
  }
}
```
## CalculationUtilクラス
2つの引数を受け取り足し算をするstaticメソッドを持つUtilクラスです。
```java:CalculationUtil.java
public class CalculationUtil {
  public static int plus(int a, int b) {
    return a + b;
  }
}
```
# テストコード
- ServiceTestクラス

## build.gradle
staticメソッドをモック化するには、特別なライブラリを利用しますので、`build.gradle`の`dependencies`に下記を追加しましょう。

`testImplementation 'org.mockito:mockito-inline:5.2.0'`を追加する。

```gradle:build.gradle
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.mockito:mockito-inline:5.2.0'
}
```

## ServiceTestクラス
テストクラスは下記のようになります！

```java:ServiceTest.java
@Import({Service.class})
@ExtendWith(SpringExtension.class)
public class ServiceTest {

  @Autowired
  private Service sut;

  @Test
  void 引数の足し算の結果が3の倍数の場合() {
    try (var mockStatic = Mockito.mockStatic(CalculationUtil.class)) {
      mockStatic.when(() -> CalculationUtil.plus(anyInt(), anyInt())).thenReturn(3);
      var actual = sut.calculateAndCheckFizzBuzz(1, 1);
      Assertions.assertEquals("Fizz", actual);
    }
  }

  @Test
  void 引数の足し算の結果が5の倍数の場合() {
    try (var mockStatic = Mockito.mockStatic(CalculationUtil.class)) {
      mockStatic.when(() -> CalculationUtil.plus(anyInt(), anyInt())).thenReturn(5);
      var actual = sut.calculateAndCheckFizzBuzz(1, 1);
      Assertions.assertEquals("Buzz", actual);
    }
  }

  @Test
  void 引数の足し算の結果が15の倍数の場合() {
    try (var mockStatic = Mockito.mockStatic(CalculationUtil.class)) {
      mockStatic.when(() -> CalculationUtil.plus(anyInt(), anyInt())).thenReturn(15);
      var actual = sut.calculateAndCheckFizzBuzz(1, 1);
      Assertions.assertEquals("FizzBuzz", actual);
    }
  }

  @Test
  void 引数の足し算の結果が上記以外の倍数の場合() {
    try (var mockStatic = Mockito.mockStatic(CalculationUtil.class)) {
      mockStatic.when(() -> CalculationUtil.plus(anyInt(), anyInt())).thenReturn(4);
      var actual = sut.calculateAndCheckFizzBuzz(1, 1);
      Assertions.assertEquals("4", actual);
    }
  }
}
```
テストの実行結果は全て成功しています！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630308/dec5e945-835a-1ba2-e7f5-fe2989372d8c.png)


# ポイント
- `Mockito.mockStatic()`の引数に、モック化したいstaticメソッドを持つCalculationUtilクラスを渡すことでモックインスタンスを作成しており、`CalculationUtil.plus()`が呼ばれたときに任意の数字を返却するようにしています。

```java
mockStatic.when(() -> CalculationUtil.plus(anyInt(), anyInt())).thenReturn(3);
```

- その結果、通常だったら足し算で2になるような引数（1,1）を渡しても、モックが効いたことで合計が3になり、期待値の”Fizz”が返却されてきている。
```java
var actual = sut.calculateAndCheckFizzBuzz(1, 1);
Assertions.assertEquals("Fizz", actual);
```

- また、生成されたモックを適用する範囲を明示的にするため、`try-with-resources`を利用しています。

https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/MockedStatic.html

>型の静的メソッドのアクティブなモックを表します。モックは、この静的モックが作成されたスレッドにのみ影響するため、このオブジェクトを別のスレッドから使​​用するのは安全ではありません。close()このオブジェクトのメソッドが呼び出されるときに、静的モックが解放されます。このオブジェクトが閉じられない場合、静的モックは開始スレッド上でアクティブなままになります。したがって、JUnit ルールや拡張機能を使用するなど、明示的に管理する場合を除き、このオブジェクトを try-with-resources ステートメント内で作成することをお勧めします。

以上、ユニットテスト作成時にstaticメソッドを簡単にモック化する方法でした！