V některých případech obsahuje třída složité metody, které však používá pouze tato třída a je zbytečné měnit jejich viditelnost jen kvůli otestování. K otestování takových metod je nutné využít reflexi.
Třída, kterou budeme testovat. Tato třída obsahuje veřejnou metodu compute() a privátní metodu getLongFromString(). Obě tyto metody budeme chtít otestovat.
public class MyClass {
public long compute(String num1, String num2) {
return Math.abs(getLongFromString(num1) - getLongFromString(num2));
}
private long getLongFromString(String number) {
return Long.parseLong(number);
}
}
Třída, která slouží jako předek všech testovacích tříd. Tato třída obsahuje metodu getPrivateMethods, která na základě třídy, názvu metody a jejich parametrů dokáže vrátit a zpřístupnit danou metodu na daném objektu.
import java.lang.reflect.Method;
public class AbstractTest {
protected Method getPrivateMethod(Class<?> clazz, String methodName, Class<?> ... parameterTypes) {
try {
// get method vrací pouze public metody
// Method method = clazz.getMethod(methodName, parameterTypes);
// proto je potřeba volat getDeclaredMethod
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
return method;
} catch (Exception e) {
throw new IllegalArgumentException("Cannot obtain method with name: " + methodName, e);
}
}
}
A nakonec třída s testy. Metodu getLongFromString testujeme dvakrát. Jednou pro případ, že zadáme správné parametry a podruhé testujeme, že vyhodí výjimku.
import static org.junit.Assert.assertTrue;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.junit.BeforeClass;
import org.junit.Test;
public class MyClassTest extends AbstractTest {
private static MyClass myClass;
@BeforeClass
public static void prepare() {
myClass = new MyClass();
}
@Test
public void computeOK() {
String n1 = "120";
String n2 = "330";
long result = myClass.compute(n1, n2);
assertTrue(result == 210);
}
@Test
public void getLongFromStringOK() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
String n = "30";
Method testedMethod = getPrivateMethod(MyClass.class, "getLongFromString", String.class);
long result = (Long) testedMethod.invoke(myClass, n);
assertTrue(result == 30);
}
@Test(expected = InvocationTargetException.class)
public void getLongFromStringThrowsException() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
String s = "AB";
Method testedMethod = getPrivateMethod(MyClass.class, "getLongFromString", String.class);
testedMethod.invoke(myClass, s);
}
}
Anotace @BeforeClass znamená, že daná metoda se volá jednou a to před voláním jakékoliv jiné metody ve třídě. Anotace @Test(expected = InvocationTargetException.class) definuje, jaký typ výjimky má být vyhozen. Metoda getLongFromString sice vyhodí NumberFormatException, ale ta pak způsobí InvocationTargetException. Z toho důvodu očekáváme při testu tuto výjimku.