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.