mockito.MockedStatic を使うときの書き分け ２つ

# はじめに

`mockedStatic` あんまり使ったことがなく、『エラーが何で起きてるか分からん！』と質問されたときに即答できなかったのが悔しかったので備忘。

# もくじ

- [mockito.MockedStatic の書き方２つ](#mockitomockedstatic-の書き方２つ)
  - [サンプルコード](#サンプルコード)
  - [メソッド参照を使う場合](#メソッド参照を使う場合)
  - [ラムダ式を使う場合](#ラムダ式を使う場合)
- [使い分け](#使い分け)
- [参考](#参考)


# 実行環境

- Java8 以降
- JUnit5
- Mockito 4.11.0

```gradle:build.gradle
dependencies {
    testImplementation('org.mockito:mockito-core:4.11.0')
    testImplementation('org.mockito:mockito-inline:4.11.0') // 3.4.0以降でないと使えない
}
```

※ `mockito-inline` は `mockito-core` の機能を含んでいるため、`mockito-inline` のみを追加しても問題ないようです

https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#mockito-inline

# mockito.MockedStatic の書き方２つ

`mockedStatic` は以下2通りの方法で書くことができます。

- メソッド参照
- ラムダ式

Javaのメソッド参照とラムダ式は `Java 8` 以降の機能になります。興味あったらそれぞれ詳しく調べてみてください。

## サンプルコード

テスト対象クラス/メソッド

```java:MyClassUtils.java
public class MyClassUtils {

    // 引数なしのstaticメソッド
	public static int staticMethodWithNoArguments() {
		return 0;
	}

    // 引数ありのstaticメソッド
	public static int staticMethodWithArguments(int input) {
		return input;
	}
```

## メソッド参照を使う場合

```java:MyClassUtilsTest.java
class MyClassUtilsTest {

	@DisplayName("staticメソッド（引数なし）をmockStatic（メソッド参照）して100を返すように振る舞いを変える")
	@Test
	void mockStaticWithMethodReferenceTest() {
		try (MockedStatic<MyClassUtils> mockedMyClassUtils = Mockito.mockStatic(MyClassUtils.class)) {
			// Arrange
			mockedMyClassUtils.when(MyClassUtils::staticMethodWithNoArguments).thenReturn(100);
			// Act & Assert
			assertThat(MyClassUtils.staticMethodWithNoArguments(), is(100));
		}
	}

	@DisplayName("staticメソッド（引数あり）をmockStatic（メソッド参照）するとコンパイルエラーになる")
	@Test
	void compileError() {
		try (MockedStatic<MyClassUtils> mockedMyClassUtils = Mockito.mockStatic(MyClassUtils.class)) {
			// Arrange
			mockedMyClassUtils.when(MyClassUtils::staticMethodWithArguments(2)).thenReturn(200);
			// "Syntax error on token "staticMethodWithArguments", AssignmentOperator expected after this token"
			// Act & Assert
			assertThat(MyClassUtils.staticMethodWithArguments(2), is(200));
		}
	}
}
```

## ラムダ式を使う場合

```java:MyClassUtilsTest.java
class MyClassUtilsTest {

    @DisplayName("staticメソッド（引数なし）をmockStatic（ラムダ式）して300を返すように振る舞いを変える")
	@Test
	void mockStaticWithLambdaExpressionTest() {
		try (MockedStatic<MyClassUtils> mockedMyClassUtils = Mockito.mockStatic(MyClassUtils.class)) {
			// Arrange
			mockedMyClassUtils.when(() -> MyClassUtils.staticMethodWithNoArguments()).thenReturn(300);
			// Act & Assert
			assertThat(MyClassUtils.staticMethodWithNoArguments(), is(300));
		}
	}

	@DisplayName("staticメソッド（引数あり）をmockStatic（ラムダ式）して400を返すように振る舞いを変える")
	@Test
	void mockStaticWithLambdaExpressionTest2() {
		try (MockedStatic<MyClassUtils> mockedMyClassUtils = Mockito.mockStatic(MyClassUtils.class)) {
			// Arrange
			mockedMyClassUtils.when(() -> MyClassUtils.staticMethodWithArguments(4)).thenReturn(400);
			// Act & Assert
			assertThat(MyClassUtils.staticMethodWithArguments(4), is(400));
		}
	}
}
```

# 使い分け

モックしたいメソッドに引数があるかどうかで判断することが１つの方法。

- 単純にメソッドを指定するだけならメソッド参照（シンプルで読みやすい記法）
- 引数に対して何らかの操作や条件分岐が必要ならラムダ式（より複雑な動作に対応できる記法）

注意：サンプルコードのように `try-with-resources` を使わないで `mockedStatic` 使う場合は必ずcloseしましょうね。

# 参考

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