Java 如何在 JUnit 4 中运行属于某个类别的所有测试
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2176570/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to run all tests belonging to a certain Category in JUnit 4
提问by Kaitsu
JUnit 4.8 contains a nice new feature called "Categories" that allows you to group certain kinds of tests together. This is very useful, e.g. to have separate test runs for slow and fast tests. I know the stuff mentioned in JUnit 4.8 release notes, but would like to know how I can actually run all the tests annotated with certain category.
JUnit 4.8 包含一个很好的新特性,称为“类别”,它允许您将某些类型的测试组合在一起。这非常有用,例如对慢速和快速测试进行单独的测试运行。我知道JUnit 4.8 发行说明中提到的内容,但想知道我如何实际运行所有带有特定类别注释的测试。
The JUnit 4.8 release notes show an example suite definition, where SuiteClasses annotation selects the tests from certain category to run, like this:
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
}
Does anyone know how I could run all the tests in SlowTests category? It seems that you must have the SuiteClasses annotation...
有谁知道我如何运行 SlowTests 类别中的所有测试?看来你必须有 SuiteClasses 注释...
采纳答案by Kaitsu
I found out one possible way to achieve what I want, but I don't consider this to be the best possible solution as it relies on ClassPathSuite library that is not part of JUnit.
我找到了一种可能的方法来实现我想要的,但我不认为这是最好的解决方案,因为它依赖于不属于 JUnit 的 ClassPathSuite 库。
I define the test suite for slow tests like this:
我为慢速测试定义了测试套件,如下所示:
@RunWith(Categories.class)
@Categories.IncludeCategory(SlowTests.class)
@Suite.SuiteClasses( { AllTests.class })
public class SlowTestSuite {
}
AllTests class is defined like this:
AllTests 类定义如下:
@RunWith(ClasspathSuite.class)
public class AllTests {
}
I had to use ClassPathSuite class from ClassPathSuiteproject here. It will find all the classes with tests.
我不得不在这里使用ClassPathSuite项目中的ClassPathSuite 类。它将找到所有带有测试的类。
回答by Jens Schauder
I am not sure, what exactly your problem is.
我不确定,你的问题到底是什么。
Just add all the tests to a suite (or hirachy of suites). Then use the Categories Runner and Include/ExcludeCategory annotation, to specify the categories you want to run.
只需将所有测试添加到一个套件(或一组套件)中。然后使用 Categories Runner 和 Include/ExcludeCategory 注释来指定要运行的类别。
A good idea might be to have one suite containing all the tests, and a couple of seperate suites referring to the first one, specifying the different set of Categories you neeed.
一个好主意可能是让一个套件包含所有测试,并且有几个单独的套件引用第一个,指定您需要的不同类别集。
回答by manuel aldana
Not a direct answer to your problem, but maybe the general approach could be improved...
不是您问题的直接答案,但也许可以改进一般方法......
Why are your tests slow? Maybe the set-up lasts long (database, I/O etc.), maybe the tests are testing too much? If this is the case I would seperate the real unit-tests from the "long-running" ones, which often indeed are integration tests.
为什么你的测试很慢?也许设置持续很长时间(数据库、I/O 等),也许测试测试太多了?如果是这种情况,我会将真正的单元测试与“长期运行”的单元测试分开,后者通常确实是集成测试。
In my setups I have staging env, where unit-tests are run often and integration-tests constantly but more rarely (e.g. after each commit in version control). I have never worked with grouping for unit tests, because they should be loosely coupled alltogether. I only work with grouping and relationship of test-cases in integration-test setups (but with TestNG).
在我的设置中,我有 staging env,单元测试经常运行,集成测试经常但很少(例如在版本控制中的每次提交之后)。我从来没有使用过单元测试的分组,因为它们应该松散耦合在一起。我只在集成测试设置中处理测试用例的分组和关系(但使用 TestNG)。
But good to know that JUnit 4.8 introduced some grouping features.
但很高兴知道 JUnit 4.8 引入了一些分组功能。
回答by Cedric Beust
Here are some of the main differences between TestNG and JUnit when it comes to groups (or categories, like JUnit calls them):
以下是 TestNG 和 JUnit 在组(或类别,如 JUnit 称之为类别)方面的一些主要区别:
JUnit's are typed (annotations) while TestNG's are strings. I made this choice because I wanted to be able to use regular expressions when running tests, for example "run all the tests that belong to the group "database*". Also, having to create a new annotation whenever you need to create a new category is annoying, although it has the benefit that an IDE will tell you right away where this category is used (TestNG shows you this in its reports).
TestNG separates very clearly your static model (the code of your tests) from the runtime model (which tests get run). If you want to run the groups "front-end" first and then "servlets", you can do this without having to recompile anything. Because JUnit defines groups in annotations and you need to specify these categories as parameters to the runner, you usually have to recompile your code whenever you want to run a different set of categories, which defeats the purpose in my opinion.
JUnit 是类型化的(注释),而 TestNG 是字符串。我做出这个选择是因为我希望能够在运行测试时使用正则表达式,例如“运行属于“数据库*”组的所有测试。此外,每当需要创建新注释时都必须创建新注释类别很烦人,尽管它的好处是 IDE 会立即告诉您使用此类别的位置(TestNG 在其报告中向您展示了这一点)。
TestNG 将您的静态模型(您的测试代码)与运行时模型(测试运行)非常清楚地分开。如果您想先运行组“前端”,然后运行“servlet”,则无需重新编译任何内容即可执行此操作。因为 JUnit 在注解中定义了组,并且您需要将这些类别指定为运行器的参数,所以每当您想要运行一组不同的类别时,通常都必须重新编译代码,在我看来,这违背了目的。
回答by Stanislau Fink
To run categorized tests without specifying all of them explicily in @Suite.SuiteClasses
annotation you can provide your own implementation of Suite.
For example a org.junit.runners.ParentRunner
can be extended. Instead of using an array of classes provided by @Suite.SuiteClasses
, new implementation should perform search for categorized tests in classpath.
要运行分类测试而不在@Suite.SuiteClasses
注释中明确指定所有测试,您可以提供自己的 Suite 实现。例如 aorg.junit.runners.ParentRunner
可以扩展。@Suite.SuiteClasses
新实现应该在类路径中搜索分类测试,而不是使用 提供的类数组。
See this projectas an example of such approach. Usage:
见这个项目作为这样的方法的一个例子。用法:
@Categories(categoryClasses = {IntegrationTest.class, SlowTest.class})
@BasePackage(name = "some.package")
@RunWith(CategorizedSuite.class)
public class CategorizedSuiteWithSpecifiedPackage {
}
回答by Kevin Wong
One downside to Kaitsu's solution is that Eclipsewill run your tests twice, and the SlowTests 3 times, when running all the tests in a project. This is because the Eclipse will run all the tests, then the AllTests suite, then the SlowTestSuite.
Kaitsu 解决方案的一个缺点是,在运行项目中的所有测试时,Eclipse将运行您的测试两次,而 SlowTests 运行 3 次。这是因为 Eclipse 将运行所有测试,然后是 AllTests 套件,然后是 SlowTestSuite。
Here is a solution that involves creating subclasses of the Kaitsu solution test runners to skip the suites unless a certain system property is set. A shameful hack, but all I have come up with so far.
这是一个解决方案,它涉及创建 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";
}
}
So, in your FastTestSuite launcher, just add -Drun.dev.unit.tests=true to the VM arguments. (Note that this solution references a fast test suite instead of a slow one.)
因此,在您的 FastTestSuite 启动器中,只需将 -Drun.dev.unit.tests=true 添加到 VM 参数即可。(请注意,此解决方案引用了快速测试套件而不是慢速测试套件。)