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

JUnit 4.8包含了一个很好的新function“类别”,它允许你将某些types的testing分组在一起。 这是非常有用的,例如对于慢速和快速testing进行单独的testing运行。 我知道在JUnit 4.8发行说明中提到的东西,但是想知道如何实际运行所有用特定类别注释的testing。

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

@RunWith(Categories.class) @IncludeCategory(SlowTests.class) @SuiteClasses( { A.class, B.class }) // Note that Categories is a kind of Suite public class SlowTestSuite { // Will run Ab and Bc, but not Aa } 

有谁知道我怎么能运行在SlowTests类别的所有testing? 看来你必须有SuiteClass注解…

我发现了一种可能的方式来实现我想要的function,但是我不认为这是最好的解决scheme,因为它依赖于不属于JUnit的ClassPathSuite库。

我为这样的慢速testing定义testing套件:

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

AllTests类是这样定义的:

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

我不得不在ClassPathSuite项目中使用ClassPathSuite类。 它会find所有的testing类。

下面是TestNG和JUnit之间在组合方面的一些主要区别(或类,就像JUnit调用它们一样):

  • JUnit是键入(注释),而TestNG是string。 我做了这个select,是因为我想在运行testing时能够使用正则expression式,例如“运行属于组的所有testing”database *“。另外,无论何时需要创build一个新的注释尽pipe它有一个好处,IDE会马上告诉你使用这个类别的地方(TestNG在它的报告中显示你)。

  • TestNG非常清楚地将您的静态模型(您的testing代码)与运行时模型(哪些testing运行)分开。 如果你想先运行组“前端”,然后再运行“servlets”,你可以做到这一点,而无需重新编译任何东西。 由于JUnit在注释中定义了组,并且需要将这些类别指定为跑步者的参数,所以当你想运行一组不同的类别时,你通常必须重新编译你的代码,这在我看来是失败的。

Kaitsu的解决scheme的一个缺点是,在项目中运行所有testing时, Eclipse将运行两次testing,SlowTests运行三次。 这是因为Eclipse将运行所有testing,然后运行AllTests套件,然后运行SlowTestSuite。

这是一个解决scheme,涉及到创buildKaitsu解决schemetesting运行器的子类来跳过套件,除非设置了某个系统属性。 可耻的黑客,但我到目前为止已经提出。

 @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参数中即可。 (请注意,这个解决scheme引用了一个快速testing套件,而不是一个慢速testing套件

要在@Suite.SuiteClasses注解中明确指定所有这些testing,可以运行分类testing,您可以提供自己的Suite实现。 例如一个org.junit.runners.ParentRunner可以被扩展。 新的实现不应该使用由@Suite.SuiteClasses提供的类的数组,而应该在classpath中执行对分类testing的search。

看到这个项目就是这种方法的一个例子。 用法:

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

我不确定你的问题到底是什么

只需将所有testing添加到套件(或套件)。 然后使用Categories Runner和Include / ExcludeCategory注释来指定要运行的类别。

一个好主意可能是有一个包含所有testing的套件,还有一个包含第一个套件的独立套件,指定您需要的不同类别集合。

不是你的问题的直接答案,但也许一般的方法可以改善…

为什么你的testing变慢? 也许设置持续很久(数据库,I / O等),也许testingtesting太多? 如果是这样的话,我会把“长期运行”的unit testing分离出来,而这些testing往往是集成testing。

在我的设置中,我已经升级了env,其中unit testing经常运行,集成testing不断,但更为罕见(例如在每次提交版本控制之后)。 我从来没有组合unit testing,因为它们应该松散耦合在一起。 我只能在集成testing设置(但是使用TestNG)中进行testing用例的分组和关系。

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