Assumptions: Java11, JUnit5, sbt
I'm tired of the monster method, so I wrote something like this ... Let's prepare it at the beginning of the project.
As a result of preparing something like this I divided the methods and reduced the number of lines per method for the time being, Instead, it could be made into a global variable by expanding the scope of the variable so that it can be referenced by various methods. There is a problem that ... Maybe it's still better to prepare this one this time? I think.
Paste ↓ in libraryDependencies of 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",
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.Methods under java package such as equals are not applicable
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%It is a monster method of s line", className + "#" + methodInfo.getName(), line));
fail = true;
}
}
}
if (fail)
throw new Exception("Monster method detected");
}
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 is a 36-line monster method
java.lang.Exception:Monster method detected
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)
Looking at the implementation of javassist.bytecode.MethodInfo # getLineNumber
,
It seems that the numerical value passed as an argument is passed to the following method.
https://github.com/jboss-javassist/javassist/blob/rel_3_25_0_ga/src/main/javassist/bytecode/LineNumberAttribute.java#L77
It seems that it is judged only by turning it in a loop and exceeding it, so
We are passing ʻInteger.MIN_VALUE and ʻInteger.MAX_VALUE
.
start and end are
100: public void method(){
101: //start is the number of lines here
102: //
103: //end is the number of lines here
104: }
Since 103-102 = 2, it is treated as a method with 1 added and 3 lines.
Older versions of Javassist don't follow the newer Java version, so make it as new as possible.
I want to recursively search the class list under the package -Qiita Javassist memo \ (Hishidama's Javassist Memo )
Recommended Posts