JUnitでprivateメソッドをテストする方法

PHPの[privateとprotectedメソッドをPHPUnitでテストする方法はこちら](https://qiita.com/ponsuke0531/items/6dc6fc34fff1e9b37901)

- 環境
    - macOS Hight Sierra
    - Eclipse : Neon.3 Release (4.6.3)
    - JUnit : 4

```java:テスト対象はprivateなメソッドです
/** 正規表現Util. */
public class SeikiHyogenUtil {
    /**
     * エスケープした記号を取得する.
     * @param kigo エスケープしていない記号.
     * @return エスケープした記号.
     */
    private String getEscapedKigo(String kigo) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < kigo.length(); i++) {
            String c = String.valueOf(kigo.charAt(i));
            sb.append(Matcher.quoteReplacement(c));
        }
        return sb.toString();
    }
}
```

```java:privateなメソッドをテストする方法
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.junit.Test;

/** SeikiHyogenUtilのテストクラス. */
public class SeikiHyogenUtilTest {
    SeikiHyogenUtil seikiHyogenUtil = new SeikiHyogenUtil();

    /**
     * テストメソッド本体.
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    @Test
    public void getEscapedKigoは引数が$の場合にエスケープした$を返却すること() throws NoSuchMethodException, SecurityException,
            IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        String param = "$";
        String expected = "\\" + param;
        String actual = getEscapedKigoを実行する(param);
        assertThat(actual, is(expected));
    }

    /**
     * privateなメソッドを実行する.
     * @param kigo privateなメソッドの引数をそのまま引数にするとわかりやすい.
     * @return privateなメソッドの戻り値.
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalAccessException invokeでthrowされる例外もそのままthrowします
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    private String getEscapedKigoを実行する(String kigo) throws NoSuchMethodException, SecurityException,
            IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        String methodName = "getEscapedKigo";
        Method method = privateメソッドを取得する(methodName);
       // method.invoke(インスタンス, 引数);で実行してメソッドの返却値に合わせて型変換する
        return (String) method.invoke(this.seikiHyogenUtil, kigo);
    }

    /**
     * privateなメソッドを取得する.
     * @param methodName privateなメソッドの名前.
     * @return privateなメソッド.
     * @throws NoSuchMethodException getDeclaredMethodでthrowされる例外はそのままthrowします
     * @throws SecurityException
     */
    private Method privateメソッドを取得する(String methodName) throws NoSuchMethodException, SecurityException {
        // クラス名.class.getDeclaredMethod(メソッドの名前, 引数の型);でメソッドを取得する
        Method method = SeikiHyogenUtil.class.getDeclaredMethod(methodName, String.class);
        // 外部からのアクセスを許可させる
        method.setAccessible(true);
        return method;
    }
}
```

## privateなメソッドをテストする方法
### ① privateメソッドを取得する
1. `getDeclaredMethod`を使ってメソッドを取得する
2. `setAccessible`で取得したメソッドのアクセスを許可させる

```java:privateメソッドを取得する
        // クラス名.class.getDeclaredMethod(メソッドの名前, 引数の型);でメソッドを取得する
        Method method = SeikiHyogenUtil.class.getDeclaredMethod(methodName, String.class);
        // 外部からのアクセスを許可させる
        method.setAccessible(true);
```

### ② privateなメソッドを実行する
1. ①で取得したメソッドの`invoke`にクラスのインスタンスと必要な引数をわたして実行する
2. `invoke`の返却値はObject型なのでメソッドの返却値に合わせて変換する

```java:privateなメソッドを実行する
        Method method = privateメソッドを取得する(methodName);
       // method.invoke(インスタンス, 引数);で実行してメソッドの返却値に合わせて型変換する
        return (String) method.invoke(this.seikiHyogenUtil, kigo);
```

## 参考にさせていただいたサイト様
- [Junitでprivateメソッドのテスト方法 - Qiita](https://qiita.com/village/items/2f0d99b25eef0c8fd4ec)
- [JUnitでprivate/protectedメソッドにアクセスするメモ - Qiita](https://qiita.com/stkdev/items/622e389e9387b75f99ad)
- [テックノート  &#8211;  Javaのprivateメソッドをjunitでテストする方法](http://javatechnology.net/java/private-junit/)