LoginSignup
0
1

More than 3 years have passed since last update.

JUnitでprivate scopeなものをテストする方法

Posted at

private scopeなものをテストする

Javaでprivate scopeのフィールド、メソッド、内部クラスを外部から呼び出し、
JUnitでテストしてみます。
※何番煎じか、出涸らしかという記事です。

まず、JUnitでテストしようとしてみます。

error.png

コンパイルエラーです。仕様です。実行できません。
privateなので、publicと同じようには呼び出せません。

そこでReflectionを使ってprivate scopeなものを呼び出します。

Reflectionとは

Reflectionとは、Javaの標準ライブラリの一種で
クラスそのものの情報を扱うことです。
具体的な例として、
クラス名、メソッド名、フィールド名の文字列で
そのクラスを生成、メソッド実行、フィールドへのアクセスが
できたりします。

以下、外部サイトですが、例えばこんな感じです。
http://java.keicode.com/lang/reflection.php

「こんなに便利なら積極的に使おう」
と思う方もいるかもしれません。
ですが、Reflectionは諸刃の剣です。取扱厳重注意です。
しかし、敢えて紹介します。

Reflectionの問題点

Reflectionは使い方次第で

  • クラス設計が崩壊する
  • コードが書きにくいし読みにくくなる
  • パフォーマンスが普通にメソッドを呼び出すより悪い

など、問題点もあります。
しかし、敢えて紹介します。

「JUnitでprivate scopeなものをテストする」
という限定した目的で使うためです。

本題に戻って

さっきのエラーだらけのJUnitのテストクラスを直してみます。

package jp.co.illmatics.sample;
/**
 * テスト対象のクラス
 */
@SuppressWarnings("unused")
public class PrivateSample {

    private String field = "Hello field!";

    private final String finalField = "Hello final field!";

    private String doIt()    {
        return "Hello method!";
    }

    private class InnerClass {
        public InnerClass() {

        }
        public String getInnerMethod() {
            return "Hello inner method!";
        }
    }
}
package jp.co.illmatics.sample;

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

import org.junit.Test;

import junit.framework.TestCase;

/**
 * テストクラス
 */
public class PrivateSampleTest extends TestCase {

    PrivateSample privateSample = new PrivateSample();
    String expected = "";
    String actual = "";

    @Test
    public void testField() throws Exception {
        final String fieldName = "field";
        expected = "Hello field!";
        actual = (String) getFieldValue(privateSample, fieldName);
        assertEquals(expected, actual);
    }

    @Test
    public void testFinalField() throws Exception {
        final String finalFieldName = "finalField";
        expected = "Hello updated final Field!";
        actual = (String) getUpdatedFinalFieldValue(privateSample, expected, finalFieldName);
        assertEquals(expected, actual);
    }

    @Test
    public void testMethod() throws Exception {
        final String methodName = "doIt";
        expected = "Hello method!";
        actual = (String) getMethodValue(privateSample, methodName);
        assertEquals(expected, actual);
    }

    @Test
    public void testInnerClassMethod() throws Exception {
        final String innerClassName = "jp.co.illmatics.sample.PrivateSample$InnerClass";
        final String innerMethodName = "getInnerMethod";
        expected = "Hello inner method!";
        actual = (String) getInnerClassMethod(privateSample, innerClassName, innerMethodName);
        assertEquals(expected, actual);
    }

    private Object getFieldValue(PrivateSample privateSample, String fieldName)
            throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
            SecurityException {
        return getPrivateField(privateSample, fieldName).get(privateSample);
    }

    private Field getPrivateField(PrivateSample privateSample, String fieldName)
            throws NoSuchFieldException, SecurityException, IllegalArgumentException,
            IllegalAccessException {
        Class<?> clazz = privateSample.getClass();
        Field field = clazz.getDeclaredField(fieldName);
        field.setAccessible(true);
        return field;
    }

    private Object getUpdatedFinalFieldValue(PrivateSample privateSample, String newValue,
            String fieldName) throws NoSuchFieldException, SecurityException,
                    IllegalArgumentException, IllegalAccessException {
        Field finalField = getPrivateField(privateSample, fieldName);
        finalField.set(privateSample, newValue);
        return finalField.get(privateSample);
    }

    private Object getMethodValue(PrivateSample privateSample, String name)
            throws NoSuchMethodException, SecurityException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {
        Method doIt = PrivateSample.class.getDeclaredMethod(name);
        doIt.setAccessible(true);
        return doIt.invoke(privateSample);
    }

    private Object getInnerClassMethod(PrivateSample parent, String classFullName,
            String methodName) throws ClassNotFoundException, NoSuchMethodException,
                    SecurityException, InstantiationException, IllegalAccessException,
                    IllegalArgumentException, InvocationTargetException {
        ClassLoader loader = ClassLoader.getSystemClassLoader();
        Class<?> innerClazz = loader.loadClass(classFullName);
        Constructor<?> constructor = innerClazz.getDeclaredConstructor(parent.getClass());
        constructor.setAccessible(true);
        Object innerObj = constructor.newInstance(parent);
        return innerClazz.getDeclaredMethod(methodName).invoke(innerObj);
    }
}

resolved.png
直せました。

testStart.png
テストします。

testSucceeded.png
できました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1