プログラミング初心者がアーキテクトっぽく語る

見苦しい記事も多数あるとは思いますが訂正しつつブログと共に成長していければと思います

Javaでprivateなフィールドやメソッドを使う

Javaで単体試験を書いているとPrivateなフィールドやメソッドにアクセスしたくなることがあります。

そんなときはコソッと修飾子をprivateからpackage privateやprotectedに変更したくなりますが絶対にやってはいけません。

危険なだけでなく、アクセス修飾子の意味に一貫性がなくなり可読性、保守性が低下します。


まずはそのprivateなフィールドやメソッドへのアクセスが本当に必要なのかもう一度、考えてみましょう。

privateなフィールドへ直接アクセスせずとも、publicなメソッドの戻り値を取得することで同じ確認が実施できないでしょうか?

publicなメソッドを試験することでprivateメソッドの試験も包含できないでしょうか?

適切に設計されたクラスならばprivateなフィールドやメソッドへのアクセスが不要なことも少なくありません。

そうでない場合はそのようにクラスやメソッドを再設計した方がよいと思います。


検討の結果、どうしてもアクセスしないといけない場合もあるでしょう。

例えば他人が書いたコードの単体試験を書いているときは再設計する余地がないかもしれません。

また、APIを作成している場合は大半のメソッドをprivateにするので、それらprivateメソッドを一切試験せず一部のHigh Levelなpublicメソッドだけでカバレッジを確保しようとすると無理がある場合もあるでしょう。

そんなときにおすすめなのが下記のようなスタティックメソッドを単体試験コードに用意しておくことです。

簡単にPrivateなフィールドやメソッドにアクセスできて試験しやすくなります。

public class TestUtil {
    public static <T> T getPrivateField(Object obj, String privateFieldName) throws NoSuchFieldException, IllegalAccessException {
        Field privateField = obj.getClass().getDeclaredField(privateFieldName);
        privateField.setAccessible(true);
        //noinspection unchecked
        return (T) privateField.get(obj);
    }

    public static Object callPrivateMethod(Object obj, String privateMethodName, Class[] paramTypes, Object[] params) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Method method = obj.getClass().getDeclaredMethod(privateMethodName, paramTypes);
        method.setAccessible(true);
        return method.invoke(obj, params);
    }
}

privateなフィールドへアクセスする例です。

List<Book> books = TestUtil.getPrivateField(shoppingCart, "books");

privateなメソッドを呼び出す例です。

TestUtil.callPrivateMethod(
        book,
        "setTitleName",
        new Class[]{String.class},
        new Object[]{"Lord Of The Ring"});