Testování private static final proměnné

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

Napsat komentář