[Java] JUnit qui NG lorsqu'une méthode avec un grand nombre de lignes est détectée en utilisant la magie noire

Hypothèses: Java11, JUnit5, sbt

J'en ai assez de la méthode des monstres, alors j'ai écrit quelque chose comme ça ... Préparons-le au début du projet.

À la suite de la préparation de quelque chose comme ça J'ai divisé les méthodes et réduit le nombre de lignes par méthode pour le moment, Au lieu de cela, il pourrait être transformé en une variable globale en élargissant la portée de la variable afin qu'elle puisse être référencée par diverses méthodes. Il y a un problème qui ... Peut-être que c'est encore mieux de préparer celui-ci cette fois? Je pense.

Coller ↓ dans la bibliothèque Dépendances de build.sbt

build.sbt


  "org.junit.jupiter" % "junit-jupiter-api" % "5.5.0",
  "org.junit.jupiter"%"junit-jupiter-engine" % "5.5.0",
  "org.javassist" % "javassist" % "3.25.0-GA",

code

package com.github.momosetkn;

import javassist.ClassPool;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;

class MonsterMethodAlert {

    @Test
    void test() throws Exception {
        var cp = ClassPool.getDefault();
        var fail = false;
        for (var className : getClassNameList()) {
            var cc = cp.get(className);
            for (var method : cc.getMethods()) {
                // java.lang.Object.Les méthodes sous le package Java telles que equals sont exclues
                if (method.getDeclaringClass().getName().startsWith("java"))
                    continue;
                var methodInfo = method.getMethodInfo();
                var start = methodInfo.getLineNumber(Integer.MIN_VALUE);
                var end = methodInfo.getLineNumber(Integer.MAX_VALUE);
                var line = end - start + 1;
                if (line >= 25) {
                    System.err.println(String.format("%s%C'est une méthode monstre de la ligne s", className + "#" + methodInfo.getName(), line));
                    fail = true;
                }
            }
        }
        if (fail)
            throw new Exception("Méthode Monster détectée");
    }

    private List<String> getClassNameList() throws IOException, URISyntaxException {
        var list = new ArrayList<String>();
        var classLoader = Thread.currentThread().getContextClassLoader();
        var targetUrls = classLoader.getResources("");
        var CLASS_EXT = ".class";
        while (targetUrls.hasMoreElements()) {
            var url = targetUrls.nextElement();
            if (!url.getProtocol().equals("file")) {
                continue;
            }
            var targetPath = Paths.get(url.toURI());
            Files.walkFileTree(targetPath, new SimpleFileVisitor<>() {
                @Override
                public FileVisitResult visitFile(Path foundPath, BasicFileAttributes attrs) throws IOException {
                    if (foundPath.toString().endsWith(CLASS_EXT)){
                        var relativizeStr = targetPath.relativize(foundPath).toString();
                        list.add(
                                relativizeStr
                                            .substring(0, relativizeStr.length() - CLASS_EXT.length())
                                            .replace(File.separatorChar, '.')
                        );
                    }
                    return super.visitFile(foundPath, attrs);
                }
            });
        }
        return list;
    }
}
Example#main est une méthode de monstre de 36 lignes

java.lang.Exception:Méthode Monster détectée

	at com.github.momosetkn.MonsterMethodAlert.test(MonsterMethodAlert.java:37)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:436)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:170)
	at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:166)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:113)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:58)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:112)
	at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$2(HierarchicalTestExecutor.java:120)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
	at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
	at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:120)
	at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$2(HierarchicalTestExecutor.java:120)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
	at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
	at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:120)
	at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:55)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:43)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:170)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:154)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:90)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Commentaire

Obtenez le nombre de lignes de méthode

En regardant l'implémentation de javassist.bytecode.MethodInfo # getLineNumber, Il semble que la valeur numérique passée à l'argument soit transmise à la méthode suivante. https://github.com/jboss-javassist/javassist/blob/rel_3_25_0_ga/src/main/javassist/bytecode/LineNumberAttribute.java#L77 Il semble qu'il ne soit jugé qu'en le tournant en boucle et en le dépassant, donc Nous transmettons ʻInteger.MIN_VALUE et ʻInteger.MAX_VALUE.

début et fin sont

100: public void method(){
101: //début est le nombre de lignes ici
102: //
103: //la fin est le nombre de lignes ici
104: }

Puisque 103-102 = 2, il est traité comme une méthode avec 1 ajouté et 3 lignes.

À propos de Javassist

Les anciennes versions de Javassist ne suivent pas la nouvelle version de Java, alors rendez-la aussi nouvelle que possible.

Matériel de référence

Je veux rechercher récursivement la liste des classes sous le package \ -Qiita Mémo Javassist \ (Mémo Javassiste de Hishidama )

Recommended Posts

[Java] JUnit qui NG lorsqu'une méthode avec un grand nombre de lignes est détectée en utilisant la magie noire
[Java] Cela peut être heureux si la valeur de retour de la méthode qui retourne null est facultative <>
Même en Java, je veux sortir vrai avec un == 1 && a == 2 && a == 3 (magie grise qui n'est pas tant que magie noire)
Créez un grand nombre d'enregistrements avec une seule commande à l'aide du fichier seeds.rb Ruby on Rails
[Lire le Java efficace] Chapitre 2 Item 2 "Considérez un constructeur face à un grand nombre de paramètres de constructeur"
[Java] Méthode de liste qui détermine si un objet spécifique est inclus
La version d'Elasticsearch que vous utilisez est-elle compatible avec Java 11?
Déclarez une méthode qui a une valeur de retour Java avec le type de données de valeur de retour
Découvrez s'il existe une police pouvant utiliser le japonais (Hiragana, Katakana, Kanji) avec AWS Lambda + Java