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ě.