Informationen zur von Junit verwendeten @ Test-Annotation Die Methode, die diese Annotation implementiert, wird als Testmethode aufgerufen. Ich habe mich gefragt, was für ein Prinzip es funktioniert Betrachten wir den Inhalt der Implementierung
Wird die von Junit gestartete Java-Datei zunächst nur die Java-Datei gestartet, die in einem bestimmten Verzeichnis angegeben ist? Wenn Sie beispielsweise eine Java-Datei unter den Einstellungsordner legen, z. B. "src / test" Starten Sie den Test ausgehend von diesem src / test.
Mit anderen Worten, es wird angenommen, dass der Untergebene von src / test als Einstiegspunkt festgelegt ist Durchlaufen Sie die Java-Dateien (Klassendateien) in diesem Verzeichnis Ich glaube, ich führe nur eine Methode aus, für die die Annotation @Test festgelegt ist (Ich kann nicht sicher sagen, weil ich die Implementierung von Junit nicht gesehen habe)
Der Grund, warum der Einstiegspunkt unter einem bestimmten Ordner wie diesem festgelegt ist Abhängig von der Klassenpfadeinstellung werden Java-Dateien in der Reihenfolge aus dem Stammpfad des Klassenladeprogramms gelesen. Suchen Sie nach der Methode mit der Annotation @Test Wenn mit Anmerkungen versehen, instanziieren Sie die Zielklasse Ich dachte, ich würde die Methode ausführen.
Basierend auf dieser Überlegung habe ich versucht zu sehen, ob eine ähnliche Implementierung durch Reflexion erreicht werden kann.
Erstellen Sie eine Annotation für die Methode, z. B. die Annotation @Test
package anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TestAnno {
boolean testFlg() default false;
}
Diesmal wurde nur die Methode in testFlg auf true gesetzt Implementiert als Ziel des Methodenaufrufs
Ich habe mit @TestAnno nach Klassen und Methoden gesucht und eine Klasse erstellt, die eingeladen werden würde.
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
import anno.TestAnno;
public class Entry {
public static void main(String[] args) throws Exception {
// *1
final String resourceName = "test";
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
final URL testRoot = classLoader.getResource(resourceName);
final String appRootStr = classLoader.getResource(".").getFile().toString();
// *2
try (Stream<Path> paths = Files.walk(Paths.get(testRoot.toURI()))) {
paths.filter(Files::isRegularFile).forEach(path -> {
// *3
String targertAbsolutePath = path.normalize().toFile().getAbsolutePath().toString();
String targetClassName = targertAbsolutePath.substring(appRootStr.length(), targertAbsolutePath.length()).replace(".class", "").replace("/", ".");
// 4
Class<?> testClass;
Object testClassInstance;
try {
testClass = Class.forName(targetClassName);
testClassInstance = testClass.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
// *5
for( Method method : testClass.getMethods() ) {
TestAnno testAnnotation = method.getAnnotation(TestAnno.class);
if ( testAnnotation == null ) continue;
try {
if ( testAnnotation.testFlg() )
method.invoke(testClassInstance);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
});
System.out.println("Test End");
}
}
}
*1 Dabei wird hier das Paket zum Laufwerkstest angegeben. Dieses Mal wird die Java-Datei unter dem Paketnamen [Test] als Ziel ausgewählt. → testRoot
*2 Verwenden Sie die Walk-Funktion, um die Java-Datei unter testRoot zu extrahieren Führen Sie eine Verzeichnisüberquerung durch. Wenn es sich um eine normale Datei handelt (dh wenn es sich um eine Klassendatei handelt), filtern Sie sie, um sie zu verarbeiten Nur Klassendateien unterliegen der Stream-Verarbeitung
*3 Hier wird die zu extrahierende Klassendatei als Zeichenfolge einschließlich des Paketnamens erfasst.
*4 Lassen Sie die Klasse als Reflexion testen Holen Sie sich eine Instanz
*5 Extrahieren Sie das Methodenobjekt aus dem erfassten Klassenobjekt und Diejenigen mit testAnnotation und testFlg von true aufrufen.
Mit dieser Prozedur konnte ich die Methoden mit @testAnnotation extrahieren.
Hier definieren wir eine Klasse mit einer Methode, die tatsächlich als Test gestartet wird.
package test;
import anno.TestAnno;
public class Test {
@TestAnno(testFlg=true)
public void testMethod() {
String className = this.getClass().getName();
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println("invoked " + className + ":" + methodName);
}
@TestAnno(testFlg=false)
public void testMethod2() {
String className = this.getClass().getName();
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println("invoked " + className + ":" + methodName);
}
@TestAnno(testFlg=true)
public void testMethod3() {
String className = this.getClass().getName();
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println("invoked " + className + ":" + methodName);
}
}
Nehmen Sie Einstellungen wie @TestAnno (testFlg = true) und vor Wenn Sie überprüfen, ob testFlg nur für Methoden eingeladen wird, die wahr sind Es hat gut funktioniert.
Das ist alles, Ich verstehe die Einstellungen und Inhalte des Kontextklassenladers nicht Ich werde es in meiner Freizeit noch einmal überprüfen.
Der schwierige Teil bei der Implementierung dieser Zeit bestand darin, den Paketnamen zu erhalten. Es besteht möglicherweise keine große Nachfrage nach einer solchen Implementierung, da nicht viele Websites referenziert werden konnten.
Dieses Mal dachte ich über den Implementierungsinhalt der Punkte nach, die ich über die Bewegung von Junit wunderte. Für Klassen und Methoden, die durch Festlegen von Anmerkungen aufgerufen werden Ich frage mich, ob eine ähnliche Implementierung vorgenommen wurde.
Zum Beispiel die automatische Verkabelung des Federgerüsts und das Scannen von Komponenten. Ich denke, es ist die gleiche Art zu denken.
Wenn ich eine Chance habe, würde ich gerne mehr über Reflexion erfahren.
Bei dieser Implementierung geht es um die Bewegung von Klassenlader und Reflexion Mir wurde klar, dass ich überhaupt nicht verstand Wenn ich einen Artikel über Java schreibe, werde ich die Details dazu untersuchen.
Viele der oben genannten Codes wurden ausgeschnitten. Bei Interesse wenden Sie sich bitte an Github. https://github.com/ktkt11122334/qiita/tree/master/test
Recommended Posts