À propos de l'annotation @Test utilisée par Junit La méthode qui implémente cette annotation est appelée comme méthode de test, Je me demandais quel genre de principe ça marche Considérons le contenu de l'implémentation
En premier lieu, le fichier Java démarré par Junit est-il uniquement le fichier Java spécifié dans un répertoire spécifique? Par exemple, si vous placez un fichier Java sous le dossier de configuration tel que "src / test" Démarrez le test à partir de ce src / test.
En d'autres termes, on pense que le subordonné de src / test est défini comme point d'entrée, donc Parcourez les fichiers Java (fichiers de classe) sous ce répertoire Je pense que j'exécute uniquement une méthode avec l'ensemble d'annotations @Test (Je ne peux pas le dire avec certitude car je n'ai pas vu l'implémentation de Junit)
La raison pour laquelle le point d'entrée est défini dans un dossier spécifique comme celui-ci En fonction du paramètre de chemin de classe, les fichiers Java sont lus dans l'ordre à partir du chemin racine du chargeur de classe. Recherchez la méthode avec l'annotation @Test Si annoté, instanciez la classe cible Je pensais que j'exécutais la méthode.
Sur la base de cette considération, j'ai essayé de voir si une implémentation similaire pouvait être réalisée en utilisant la réflexion.
Créez une annotation à donner à la méthode, telle que l'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;
}
Cette fois, seule la méthode définie sur true dans testFlg Implémenté pour être la cible de l'appel de méthode
J'ai recherché des classes et des méthodes avec @TestAnno et créé une classe qui serait invitée.
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 Dans le processus ici, le package à tester est spécifié. Cette fois, le fichier java sous le nom de package [test] est ciblé. → testRoot
*2 Utilisez la fonction walk pour extraire le fichier java sous testRoot Faites une traversée de répertoire. S'il s'agit d'un fichier normal (c'est-à-dire s'il s'agit d'un fichier de classe), filtrez-le pour le traiter Seuls les fichiers de classe sont soumis au traitement Stream
*3 Ici, le fichier de classe à extraire est acquis sous forme de chaîne de caractères comprenant le nom du package.
*4 Faites tester la classe comme une réflexion Obtenez une instance
*5 Extraire l'objet méthode de l'objet classe acquis et Ceux avec testAnnotation et testFlg de true invoquer.
Avec cette procédure, j'ai pu extraire les méthodes avec @testAnnotation.
Ici, nous définissons une classe qui a une méthode qui est réellement lancée en tant que test.
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);
}
}
Définissez des paramètres tels que @TestAnno (testFlg = true) et Si vous vérifiez si testFlg n'est invité que pour les vraies méthodes Cela a bien fonctionné.
C'est tout, Je ne comprends pas les paramètres et le contenu du chargeur de classe de contexte, donc Je vais le vérifier à nouveau pendant mon temps libre.
La partie difficile de la mise en œuvre cette fois-ci était d'obtenir le nom du package. Il se peut qu'il n'y ait pas beaucoup de demande pour une telle implémentation, car il n'y avait pas beaucoup de sites à référencer.
Cette fois, je pensais au contenu de la mise en œuvre des points que je m'interrogeais sur le mouvement de Junit. Pour les classes et méthodes appelées en définissant des annotations Je me demande si une mise en œuvre similaire a été faite.
Par exemple, le câblage automatique du cadre à ressort et la numérisation des composants. Je pense que c'est la même façon de penser.
Aussi, si j'ai une chance, j'aimerais en savoir plus sur la réflexion.
Avec cette implémentation, sur le mouvement du chargeur de classe et la réflexion J'ai réalisé que je ne comprenais pas du tout Lorsque j'écrirai un article sur Java, j'examinerai les détails qui l'entourent.
Bon nombre des codes ci-dessus ont été supprimés. Si vous êtes intéressé, veuillez vous référer à Github. https://github.com/ktkt11122334/qiita/tree/master/test
Recommended Posts