在Eclipse的JUnit视图中按顺序排列单元测试

44

在 Eclipse 中,JUnit 视图似乎会随机排序测试用例。如何按类名排序呢?


7
这让我在 Eclipse 中感到非常恼火,我希望有人能将其作为插件选项添加进去。我希望所接受的答案实际上能够解决这个问题! - matt b
7个回答

7

正如Gary在评论中所说:

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

我确实查看了,但没有发现任何有关对这些名称进行排序的功能。我建议向JUnit插件提出更改请求,但我认为使用这个插件的人不多,因此:自己动手吧。

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


是的,最好的解决方案可能是修改插件代码。但愿我有时间。 - Gary Kephart

3
一件事情是可以使用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):
@FixMethodOrder(MethodSorters.DEFAULT)-基于内部比较器的确定性顺序 @FixMethodOrder(MethodSorters.NAME_ASCENDING)-方法名称按升序排列 @FixMethodOrder(MethodSorters.JVM)-基于反射的顺序的4.11之前的方式
这很愚蠢,但解释了为什么人们开始使用test1TestName架构。
更新2: 我使用ASM,因为Javassist也会在getMethods()上产生随机排序的方法。他们在内部使用Maps。对于ASM,我只使用Visitor。
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);
    }
}
}

将这个文件放在src/test/java文件夹中的org.junit.runners.model包中。现在将junit 4.5库的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,然后将您确定为公共并具有使用test注释的方法的所有类添加到其中。
  • 添加一个getName()方法,仅返回您的套件运行程序表示的类的包名称(因此您会将树视为包树,而不是套件的类名)。
  • 检查子目录是否存在某个套件类(我使用AllTests作为所有套件类)。
  • 如果在子目录中找不到套件类,请检查其所有子目录,这样您就不会错过包含测试的包,即使父目录不包含套件也是如此。

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

就是这样。这应该可以给你足够的内容来开始并扩展它。测试套件运行器仅使用反射,但我按子目录中的测试类和套件按字母顺序排序,并将子目录(代表它们所在的包)的套件排序到最上面。


2

Mark写道:

它根据执行时间对它们进行排序, 或许你应该对你的方法进行排序? 源/排序成员

Mark是正确的。但是你不能对单元测试进行排序。不允许猜测执行顺序。

单元测试必须独立构建,调用顺序是随机的。

在大多数情况下,测试方法按字母顺序排序。类是随机的。尝试使用TestSuite来对你的测试进行排序。


嗯,我正在尝试避免使用TestSuite。我欣赏单元测试的独立性,并在创建它们时保持这种独立性,但如果Unit Runner可以按类名排序,那就太好了。嗯,也许我应该查看源代码... - Gary Kephart

2

2
11年后,JUnit视图已经具有名称排序功能,并且刚刚获得了“执行时间”排序功能。
请参见Eclipse 4.17 (2020-09)

按执行时间对测试结果进行排序

JUnit视图现在提供了按执行时间对结果进行排序的功能。

默认情况下,结果将按执行顺序排序。
从JUnit视图菜单中选择Sort By > Execution Time将在所有测试完成后重新排序结果。

当测试仍在运行时,它们将按执行顺序显示。

https://www.eclipse.org/eclipse/news/4.17/images/junit-sort-time-before.png

按执行顺序排序的结果为:

https://www.eclipse.org/eclipse/news/4.17/images/junit-sort-time-after.png


2
如果您确实需要在JUnit测试之间建立硬依赖关系,请尝试使用JExample扩展。JExample引入了生产者-消费者关系来进行单元测试。生产者是一个测试方法,它将其被测试的单元作为返回值。消费者是一个测试方法,它依赖于一个或多个生产者和他们的返回值。
您可以在Eclipse中安装它,适用于Junit4.4或4.5。
import jexample.Depends;

  @Test
  @Depends("#testEmpty") 
  public Stack<Integer> testPush(Stack<Integer> $) { 
      $.push(42); 
      assertFalse($.isEmpty()); 
      return $; 
  } 

如IBM文章"追求代码质量:JUnit 4 vs. TestNG"所述:
JUnit框架试图实现的一件事情是测试隔离。然而,这使得指定测试用例执行顺序非常困难,而测试依赖于此顺序。开发人员采用了不同的技术来解决这个问题,比如按字母顺序指定测试用例或者大量依赖于夹具(@Before @After)来正确设置事物。
这些解决方法对于成功的测试是可以的,但对于失败的测试,它们有一个不方便的后果:每个后续的依赖测试也会失败。在某些情况下,这可能导致大型测试套件报告不必要的失败。
因此,请注意:如果您保留任何解决方案以按照所需方式排序Junit测试...你需要考虑该解决方案是否支持“跳过”功能,以便允许其他测试继续进行,即使其中一个测试失败。

2
我认为他只是想按特定顺序查看结果。 - guerda
1
是的,因此我的答案是:由于您无法对结果进行排序,可以尝试对测试进行排序。 - VonC
2
是的,只需要结果。只是因为我可能想在单元测试运行后查找特定的测试结果。 我不确定为什么Eclipse不能按照它们被运行的顺序排序测试结果。只需使用SortedSet即可。 - Gary Kephart

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接