如何在JUnit 4中运行属于某个类别的所有测试

Kai*_*tsu 71 java junit unit-testing junit4

JUnit 4.8包含一个名为"Categories"的新功能,允许您将某些类型的测试组合在一起.这非常有用,例如,为慢速和快速测试分别进行测试.我知道JUnit 4.8发行说明中提到的内容,但是想知道我如何实际运行所有使用某些类别注释的测试.

JUnit 4.8发行说明显示了一个示例套件定义,其中SuiteClasses注释选择要运行的特定类别的测试,如下所示:

@RunWith(Categories.class)
@IncludeCategory(SlowTests.class)
@SuiteClasses( { A.class, B.class }) // Note that Categories is a kind of Suite
public class SlowTestSuite {
  // Will run A.b and B.c, but not A.a
}
Run Code Online (Sandbox Code Playgroud)

有谁知道如何在SlowTests类别中运行所有测试?您似乎必须拥有SuiteClasses注释...

Kai*_*tsu 60

我找到了一种可能的方法来实现我想要的,但我不认为这是最好的解决方案,因为它依赖于不属于JUnit的ClassPathSuite库.

我为这样的慢速测试定义了测试套件:

@RunWith(Categories.class)
@Categories.IncludeCategory(SlowTests.class)
@Suite.SuiteClasses( { AllTests.class })
public class SlowTestSuite {
}
Run Code Online (Sandbox Code Playgroud)

AllTests类定义如下:

@RunWith(ClasspathSuite.class)
public class AllTests {
}
Run Code Online (Sandbox Code Playgroud)

我不得不在这里使用ClassPathSuite项目中的ClassPathSuite类.它会找到所有带有测试的类.

  • 这实际上是一个非常合理的解决方案.感谢您提出自己的问题,因为这是一个非常好的问题:-) (4认同)
  • 作为对我的问题http://stackoverflow.com/q/2698174/59470的补充和详细解释,我添加了一个博客条目:http://novyden.blogspot.com/2011/06/using-junit-4-categories-到replace.html (3认同)

Ced*_*ust 7

以下是TestNG和JUnit在组(或类别,如JUnit调用它们)时的一些主要区别:

  • JUnit是键入的(注释),而TestNG是字符串.我做出了这个选择,因为我希望能够在运行测试时使用正则表达式,例如"运行属于组的所有测试"数据库*".此外,无论何时需要创建新的注释,都必须创建新的注释类别很烦人,虽然它有一个好处,IDE会立即告诉你这个类别的使用位置(TestNG在其报告中向您显示).

  • TestNG非常清楚地将运行模型(测试运行)中的静态模型(测试代码)分开.如果要先运行组"前端"然后再运行"servlets",则可以执行此操作而无需重新编译任何内容.因为JUnit在注释中定义了组,并且您需要将这些类别指定为运行器的参数,所以每当您想要运行不同的类别集时,通常都需要重新编译代码,这在我看来会失败.


Kev*_*ong 5

Kaitsu解决方案的一个缺点是Eclipse将在项目中运行所有测试时两次运行测试,而SlowTests运行3次.这是因为Eclipse将运行所有测试,然后运行AllTests套件,然后运行SlowTestSuite.

这是一个解决方案,涉及创建Kaitsu解决方案测试运行器的子类以跳过套件,除非设置了某个系统属性.一个可耻的黑客,但到目前为止我已经想到了.

@RunWith(DevFilterClasspathSuite.class)
public class AllTests {}
Run Code Online (Sandbox Code Playgroud)

.

@RunWith(DevFilterCategories.class)
@ExcludeCategory(SlowTest.class)
@SuiteClasses(AllTests.class)
public class FastTestSuite
{
}
Run Code Online (Sandbox Code Playgroud)

.

public class DevFilterCategories extends Suite
{
    private static final Logger logger = Logger
        .getLogger(DevFilterCategories.class.getName());
    public DevFilterCategories(Class<?> suiteClass, RunnerBuilder builder) throws InitializationError {
        super(suiteClass, builder);
        try {
            filter(new CategoryFilter(getIncludedCategory(suiteClass),
                    getExcludedCategory(suiteClass)));
            filter(new DevFilter());
        } catch (NoTestsRemainException e) {
            logger.info("skipped all tests");
        }
        assertNoCategorizedDescendentsOfUncategorizeableParents(getDescription());
    }

    private Class<?> getIncludedCategory(Class<?> klass) {
        IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class);
        return annotation == null ? null : annotation.value();
    }

    private Class<?> getExcludedCategory(Class<?> klass) {
        ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class);
        return annotation == null ? null : annotation.value();
    }

    private void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError {
        if (!canHaveCategorizedChildren(description))
            assertNoDescendantsHaveCategoryAnnotations(description);
        for (Description each : description.getChildren())
            assertNoCategorizedDescendentsOfUncategorizeableParents(each);
    }

    private void assertNoDescendantsHaveCategoryAnnotations(Description description) throws InitializationError {           
        for (Description each : description.getChildren()) {
            if (each.getAnnotation(Category.class) != null)
                throw new InitializationError("Category annotations on Parameterized classes are not supported on individual methods.");
            assertNoDescendantsHaveCategoryAnnotations(each);
        }
    }

    // If children have names like [0], our current magical category code can't determine their
    // parentage.
    private static boolean canHaveCategorizedChildren(Description description) {
        for (Description each : description.getChildren())
            if (each.getTestClass() == null)
                return false;
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

.

public class DevFilterClasspathSuite extends ClasspathSuite
{
    private static final Logger logger = Logger
        .getLogger(DevFilterClasspathSuite.class.getName());
    public DevFilterClasspathSuite(Class<?> suiteClass, RunnerBuilder builder) 
        throws InitializationError {
        super(suiteClass, builder);
        try
        {
            filter(new DevFilter());
        } catch (NoTestsRemainException e)
        {
            logger.info("skipped all tests");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

.

public class DevFilter extends Filter
{
    private static final String RUN_DEV_UNIT_TESTS = "run.dev.unit.tests";

    @Override
    public boolean shouldRun(Description description)
    {
        return Boolean.getBoolean(RUN_DEV_UNIT_TESTS);
    }

    @Override
    public String describe()
    {
        return "filter if "+RUN_DEV_UNIT_TESTS+" system property not present";
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,在FastTestSuite启动器中,只需将-Drun.dev.unit.tests = true添加到VM参数即可.(请注意,此解决方案引用了快速测试套件,而不是慢速测试套件.)