在 Eclipse 的 JUnit 视图中对单元测试进行排序

2022-09-01 01:10:02

Eclipse 中的 JUnit 视图似乎对测试进行随机排序。如何按类名排序?


答案 1

正如加里在评论中所说:

如果可以告诉Unit Runner继续按类名排序,那就太好了。嗯,也许我应该看看源代码...

我确实看了看,但没有提示对这些名称进行排序的功能。我会建议对JUnit插件提出更改请求,但我不认为有很多人使用这个东西,所以:DIY。

如果您修改插件代码,我希望看到解决方案。


答案 2

人们可能做的一件事是使用JUnit 3.x的模式。我们使用了一个名为 AllTests 的测试套件,您可以在其中按特定顺序将测试添加到其中。对于每个包裹,我们都有另一个AllTests。为这些测试套件指定一个与软件包相同的名称,使人们能够轻松构建一个应该由 junit 插件重视的层次结构。

我真的很不喜欢它甚至在Junit查看器中呈现测试方法的方式。它的顺序应与在 TestCase 类中指定的顺序完全相同。我以重要性和功能的方式订购这些方法。因此,最失败的方法是首先在测试用例的后半部分进行更正,然后更正更特殊的方法。

这真的很烦人,测试运行者正在加扰这些。我会自己看一看,如果我找到解决方案,我会更新这个答案。

更新:

我在TestCase中对方法名称进行排序的问题与此有关:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7023180(感谢Oracle!

因此,最终oracle更改了class.getMethods或class.getDeclaredMethods调用中方法的顺序。现在,这些方法是随机的,可以在JVM的不同运行之间更改。它与比较的优化有关,甚至是试图压缩方法名称 - 谁知道... 。

那么还剩下什么。第一个可以使用:@FixMethodOrder(来自 javacodegeeks.com):

  1. @FixMethodOrder(MethodSorters.DEFAULT) – 基于内部比较器的确定性顺序
  2. @FixMethodOrder(MethodSorters.NAME_ASCENDING) – 方法名称的升序
  3. @FixMethodOrder(MethodSorters.JVM) – 4.11 之前的基于反射的顺序

好吧,这是愚蠢的,但解释了为什么人们开始使用test1TestName模式。

更新2

我使用ASM,因为Javassist也在getMethods()上生成随机排序方法。他们在内部使用地图。对于ASM,我只使用访客。

package org.junit.runners.model;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import com.flirtbox.ioc.OrderTest;

/**
 * @author Martin Kersten
*/
public class TestClassUtil {
public static class MyClassVisitor extends ClassVisitor {
    private final List<String> names;
    public MyClassVisitor(List<String> names) {
        super(Opcodes.ASM4);
        this.names = names;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc,
            String signature, String[] exceptions) {
        names.add(name);
        return super.visitMethod(access, name, desc, signature, exceptions);
    }
}

private static List<String> getMethodNamesInCorrectOrder(Class<?> clazz) throws IOException {
    InputStream in = OrderTest.class.getResourceAsStream("/" + clazz.getName().replace('.', '/') + ".class");
    ClassReader classReader=new ClassReader(in);
    List<String> methodNames = new ArrayList<>();
    classReader.accept(new MyClassVisitor(methodNames), 0);
    return methodNames;
}

public static void sort(Class<?> fClass, List<FrameworkMethod> list) {
    try {
        final List<String> names = getMethodNamesInCorrectOrder(fClass);
        Collections.sort(list, new Comparator<FrameworkMethod>() {
            @Override
            public int compare(FrameworkMethod methodA, FrameworkMethod methodB) {
                int indexA = names.indexOf(methodA.getName());
                int indexB = names.indexOf(methodB.getName());
                if(indexA == -1)
                    indexA = names.size();
                if(indexB == -1)
                    indexB = names.size();
                return indexA - indexB;
            }
        });
    } catch (IOException e) {
        throw new RuntimeException("Could not optain the method names of " + fClass.getName() + " in correct order", e);
    }
}
}

只需将其放在 org.junit.runners.model 包中的 src/test/java 文件夹中即可。现在,将 junit 4.5 lib 的 org.junit.runners.model.TestClass 复制到同一个包中,并通过添加排序例程来更改其构造函数。

 public TestClass(Class<?> klass) {
    fClass= klass;
    if (klass != null && klass.getConstructors().length > 1)
        throw new IllegalArgumentException(
                "Test class can only have one constructor");

    for (Class<?> eachClass : getSuperClasses(fClass))
        for (Method eachMethod : eachClass.getDeclaredMethods())
            addToAnnotationLists(new FrameworkMethod(eachMethod));

            //New Part
    for(List<FrameworkMethod> list : fMethodsForAnnotations.values()) {
        TestClassUtil.sort(fClass, list);
    }

    //Remove once you have verified the class is really picked up
    System.out.println("New TestClass for " + klass.getName());

}

给你。现在,您已经按照它们在 java 文件中声明的顺序对方法进行了很好的排序。如果您想知道类路径通常以这种方式设置,则类装入器首先考虑src(目标或bin)文件夹中的所有内容。因此,在定义完全相同的包和相同的类时,您可以“覆盖”您使用的任何库中的每个类/接口。这就是诀窍!

更新3我能够以正确的顺序获得每个包和每个类的树视图。

  • 这个想法是子类ParentRunner,然后向它添加所有你标识为公共的类,并用测试来注释方法。
  • 添加一个 getName() 方法,仅返回套件运行器所表示的类的包名称(因此,您会将该树视为没有套件类名的包树)。
  • 如果找到某个套件类,请检查子目录(我对所有套件类使用AllTests)。
  • 如果在子目录中找不到套件类,请检查其所有子目录,这样,如果父目录不包含套件,则不会错过包含测试的包。

就是这样。我到处添加的套房类是:@RunWith(MySuiteRunner.class) public class AllTests { }

就是这样。你应该给足够的时间来开始和扩展这个。套件运行器仅使用反射,但我按字母顺序对子目录的测试类和套件进行排序,并且子目录的套装(表示它们所在的包)的套装排序最多。


推荐