Při testování se můžeme setkat s tím, že potřebujeme otestovat funkcionalitu, která není přes vystavené API přístupná. Jako příklad zde uvedu třídu Extractor. Jedná se o třídu, která čte soubor z přesně zadaného umístění. Z toho důvodu je soubor zadán jako private static final. Jak otestovat metodu getContent(), když nemáme přístup k souboru C:\input.txt (nemůžeme tento soubor modifikovat a dokonce tento soubor ani nemusí v době testů existovat)? Řešením je použít reflexi.
Extractor.java
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; public class Extractor { private static final File INPUT = new File("C:\\input.txt"); public Extractor() {} public String getContent() { String result = ""; try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(INPUT), "UTF-8"))) { String line; while ((line = br.readLine()) != null) { result = result + line; } } catch (IOException ioe) { ioe.printStackTrace(); } return result; } }
Testovací soubor testinput.txt umístěný v src/test/resources.
První řádek. Druhý řádek. Čtvrtý řádek.
ExtractorTest.java
import static org.junit.Assert.assertEquals; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.URL; import org.junit.Test; import cz.vifo.reflexe.Extractor; public class ExtractorTest { @Test public void test() { ClassLoader classLoader = ExtractorTest.class.getClassLoader(); URL resource = classLoader.getResource("testinput.txt"); File testFile = new File(resource.getFile()); try { setPrivateStaticFinalField(Extractor.class.getDeclaredField("INPUT"), testFile); Extractor extractor = new Extractor(); String content = extractor.getContent(); assertEquals("První řádek.Druhý řádek.Čtvrtý řádek.", content); } catch (Exception e) { throw new RuntimeException("Error when mocking Extractor class", e); } } private void setPrivateStaticFinalField(Field field, Object newValue) throws Exception { // accessing private field.setAccessible(true); // changing final Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); // we change static field so we pass null field.set(null, newValue); } }
Nyní si kód rozebereme podrobněji.
ClassLoader classLoader = ExtractorTest.class.getClassLoader(); URL resource = classLoader.getResource("testinput.txt"); File testFile = new File(resource.getFile());
Získáme testovací soubor.
Extractor.class.getDeclaredField("INPUT")
Získáme proměnnou INPUT.
field.setAccessible(true);
Jedná se private proměnnou. Proto je třeba jí povolit přístup. Toto by nám nyní stačilo pro získání dat proměnné.
Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
Proměnná má modifikátor final. Pokud bychom neprovedli změnu, nemohli bychom final proměnnou změnit.
field.set(null, newValue);
Do statické proměnné přiřadíme novou hodnotu. Pokud test spustíme, proběhne úspěšně.