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


72

JUnit 4.8包含一个很好的新功能,称为“类别”,使您可以将某些类型的测试组合在一起。这非常有用,例如为慢速和快速测试分别进行测试。我知道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
}

有谁知道我可以如何在SlowTests类别中运行所有测试?看来您必须具有SuiteClasses批注...


1
你好 我有一个相关的问题。随意附和: stackoverflow.com/questions/15776718/...
amphibient

它没有直接关系,但是这是一种计算“类别测试”计数器的方法
Bsquare

Answers:


62

我发现了一种实现我想要的目标的可能方法,但是我认为这不是最好的解决方案,因为它依赖于JPath之外的ClassPathSuite库。

我为慢速测试定义了测试套件,如下所示:

@RunWith(Categories.class)
@Categories.IncludeCategory(SlowTests.class)
@Suite.SuiteClasses( { AllTests.class })
public class SlowTestSuite {
}

AllTests类的定义如下:

@RunWith(ClasspathSuite.class)
public class AllTests {
}

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


4
这实际上是一个相当合理的解决方案。感谢您回答自己的问题,因为这是一个非常好的问题:-)
Konrad'ktoso'Malawski 2011年

对于想知道如何使用Ant自动运行类别的测试(具有此精确设置)的任何人,此问题可能很有用。
Jonik

3
作为一种恭维我的问题stackoverflow.com/q/2698174/59470:和详细的解释我加了一个博客条目novyden.blogspot.com/2011/06/...
topchef

它没有直接关系,但是这是一种计算“类别测试”计数器的方法
Bsquare

7

在组(或类别,如JUnit称呼它们)方面,这是TestNG和JUnit之间的一些主要区别:

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

  • TestNG非常清楚地将您的静态模型(测试的代码)与运行时模型(运行测试的模型)分开。如果要先运行“前端”组,然后运行“ servlet”,则无需重新编译任何程序即可。因为JUnit在注释中定义了组,并且您需要将这些类别指定为运行程序的参数,所以通常每当您要运行一组不同的类别时就必须重新编译代码,这违背了我的意图。


我们以与JUnit非常相似的方式在JUnit测试中构建了自己的类别支持,主要区别在于,我们可以通过system属性来配置@ category.includeCategory批注,而不是@ Categories.IncludeCategory批注。有人猜测为什么JUnit这么难为我们做。
Trejkaz 2012年

6

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

这是一个解决方案,涉及创建Kaitsu解决方案测试运行程序的子类以跳过套件,除非设置了特定的系统属性。可耻的骇客,但到目前为止,我所能提出的一切。

@RunWith(DevFilterClasspathSuite.class)
public class AllTests {}

@RunWith(DevFilterCategories.class)
@ExcludeCategory(SlowTest.class)
@SuiteClasses(AllTests.class)
public class FastTestSuite
{
}

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;
    }
}

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");
        }
    }
}

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";
    }
}

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


2

要运行分类测试而不在@Suite.SuiteClasses批注中明确指定所有测试,可以提供自己的Suite实现。例如,org.junit.runners.ParentRunner可以扩展。而不是使用由提供的类的数组@Suite.SuiteClasses,新的实现应在类路径中执行对分类测试的搜索。

请参阅此项目作为这种方法的示例。用法:

@Categories(categoryClasses = {IntegrationTest.class, SlowTest.class})
@BasePackage(name = "some.package")
@RunWith(CategorizedSuite.class)
public class CategorizedSuiteWithSpecifiedPackage {

}

1

我不确定,您到底是什么问题。

只需将所有测试添加到一个套件(或多个套件)中即可。然后使用Categories Runner和Include / ExcludeCategory批注指定要运行的类别。

一个好主意可能是要有一个包含所有测试的套件,以及两个分别引用第一个套件的套件,以指定您需要的不同类别集。


19
我的问题是我有成千上万的测试,并且我不想手动将它们添加到任何套件中。我只希望运行具有特定类别的测试。对于JUnit来说,找出哪些测试具有一定的注释应该并不困难,因为它在查找测试方法时实际上会这样做。
Kaitsu 2010年

0

这不是您问题的直接答案,但也许可以改进一般方法...

为什么测试速度慢?设置可能会持续很长时间(数据库,I / O等),或者测试是否测试过多?如果是这种情况,我会将真实的单元测试与“长期运行的”单元测试分开,后者通常确实是集成测试。

在我的设置中,我有一个暂存环境,其中单元测试经常运行,而集成测试则不断但很少(例如,在版本控制中的每次提交之后)。我从来没有为单元测试分组,因为它们应该松散地耦合在一起。我只在集成测试设置中使用测试用例的分组和关系(但使用TestNG)。

但是很高兴知道JUnit 4.8引入了一些分组功能。


1
感谢Manuel的意见!我确实不需要分离单元测试,但是我也将JUnit用于集成测试,并且希望将它们与单元测试分离。我也看过TestNG,它似乎使测试(而不仅仅是单元测试)比JUnit更好。而且它还有更好的文档和一本好书。
Kaitsu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.