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

JUnit 4.8包含一个称为“类别”的新特性,它允许您将某些类型的测试分组在一起。这是非常有用的,例如,对于慢速和快速测试有单独的测试运行。我知道 JUnit 4.8版本说明中提到的内容,但是我想知道如何实际运行用特定类别注释的所有测试。

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

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

有人知道我如何运行“慢速测试”类别中的所有测试吗?似乎您必须具有 SuiteClass 注释..。

32143 次浏览

我不知道你到底有什么问题。

只需将所有测试添加到一个套件(或套件的层次结构)。然后使用“类别运行器”和“包含/排除类别”注释来指定要运行的类别。

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

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

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

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

AllTest 类的定义如下:

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

我必须在这里使用 类别路径套件项目中的 ClassPathSuite 类,它会找到所有带有测试的类。

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

为什么你的测试很慢?也许设置持续时间很长(数据库、 I/O 等等) ,也许测试太多了?如果是这种情况,我将把真正的单元测试与“长时间运行”的单元测试分开,后者通常是集成测试。

在我的设置中,我有阶段化 env,单元测试经常运行,集成测试经常运行,但很少(例如在版本控制中每次提交之后)。我从未处理过单元测试的分组,因为它们应该是松散耦合的。我只在集成测试设置中处理测试用例的分组和关系(但使用 TestNG)。

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

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

  • JUnit 是类型化的(注释) ,而 TestNG 是字符串。我之所以做出这个选择,是因为我希望在运行测试时能够使用正则表达式,例如“运行属于组“ database *”的所有测试”。此外,每当需要创建新类别时,都必须创建一个新注释,这很烦人,尽管这样做的好处是,IDE 会立即告诉您这个类别在哪里使用(TestNG 在其报告中向您展示这一点)。

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

要运行分类测试而不在 @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 {


}

Kaitsu 解决方案的一个缺点是,在运行项目中的所有测试时,日食将运行两次测试,而 SlowTest3次。这是因为 Eclipse 将运行所有的测试,然后是 AllTest 套件,然后是 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 启动程序中,只需向 VM 参数添加-Drun.dev.unit.test.true = 。(注意,此解决方案引用了快速测试套件,而不是慢速测试套件。)