java 运行 JUnit @Test 方法的子集

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1230706/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 15:44:34  来源:igfitidea点击:

running a subset of JUnit @Test methods

javaunit-testingreflectionjunit

提问by George

We're using JUnit 4 to test: we have classes that don't are a subclass of TestCase, and they have public methods annotated with @Test. We have one file with many @Testmethods. It would be nice to be able to run a subset of them via Ant from the command line, in the style of this recipe for JUnit 3:

我们正在使用 JUnit 4 进行测试:我们有一些不是 的子类的类TestCase,并且它们有用@Test. 我们有一个包含多种@Test方法的文件。能够从命令行通过 Ant 运行它们的子集会很好,按照 JUnit 3 的这个秘籍的风格:

ant runtest -Dtest=MyTest -Dtests=testFoo,testBar

http://today.java.net/pub/a/today/2003/09/12/individual-test-cases.html

http://today.java.net/pub/a/today/2003/09/12/individual-test-cases.html

I've been trying to think of ways to achieve this with Java reflection, etc. Since there doesn't seem to be any way to "hide" @Testmethods or remove their annotations at runtime, the only option seems to be using the ClassLoader's defineClassmethod, which seems quite difficult.

我一直在想办法用 Java 反射等来实现这一点。由于似乎没有任何方法可以@Test在运行时“隐藏”方法或删除它们的注释,唯一的选择似乎是使用 ClassLoader 的defineClass方法,这似乎相当困难。

P.S. The Right Thing in this situation would be to split up the file, but are there alternatives?

PS 在这种情况下正确的做法是拆分文件,但有其他选择吗?

Thanks for your time.

谢谢你的时间。

采纳答案by George

guerda's solution is good. Here's what I ended up doing (it's a mix of Luke Francl's recipe, which I linked before, and some other stuff I saw on the net):

古尔达的解决方案很好。这是我最终做的(这是我之前链接的 Luke Francl 的食谱和我在网上看到的其他一些东西的混合):

import org.junit.runner.manipulation.Filter;
import org.junit.runner.Description;

public final class AntCLFilter extends Filter {
    private static final String TEST_CASES = "tests";
    private static final String ANT_PROPERTY = "${tests}";
    private static final String DELIMITER = "\,";
    private String[] testCaseNames;

    public AntCLFilter() {
        super();
        if (hasTestCases()) testCaseNames = getTestCaseNames();
    }

    public String describe() {
        return "Filters out all tests not explicitly named in a comma-delimited list in the system property 'tests'.";
    }

    public boolean shouldRun(Description d) {
        String displayName = d.getDisplayName();
        // cut off the method name:
        String testName = displayName.substring(0, displayName.indexOf('('));
        if (testCaseNames == null) return true;

        for (int i = 0; i < testCaseNames.length; i++)
            if (testName.equals(testCaseNames[i]))
                return true;
        return false;
    }

    /**
     * Check to see if the test cases property is set. Ignores Ant's
     * default setting for the property (or null to be on the safe side).
     **/
    public static boolean hasTestCases() {
        return
            System.getProperty( TEST_CASES ) == null ||
            System.getProperty( TEST_CASES ).equals( ANT_PROPERTY ) ?
            false : true;
    }

    /**
     * Create a List of String names of test cases specified in the
     * JVM property in comma-separated format.
     *
     * @return a List of String test case names
     *
     * @throws NullPointerException if the TEST_CASES property
     * isn't set
     **/
    private static String[] getTestCaseNames() {

        if ( System.getProperty( TEST_CASES ) == null ) {
            throw new NullPointerException( "Test case property is not set" );
        }

        String testCases = System.getProperty( TEST_CASES );
        String[] cases = testCases.split(DELIMITER);

        return cases;
    }
}

import org.junit.internal.runners.*;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.NoTestsRemainException;

public class FilteredRunner extends TestClassRunner {

    public FilteredRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
        Filter f = new AntCLFilter();
        try {
            f.apply(this);
        } catch (NoTestsRemainException ex) {
            throw new RuntimeException(ex);
        }
    }
}

Then I annotated my test class with:

然后我用以下注释我的测试类:

@RunWith(FilteredRunner.class)
public class MyTest {

and put the following in my ant buildfile:

并将以下内容放入我的 ant 构建文件中:

<target name="runtest"
        description="Runs the test you specify on the command line with -Dtest="
        depends="compile, ensure-test-name">
    <junit printsummary="withOutAndErr" fork="yes">
        <sysproperty key="tests" value="${tests}" />
        <classpath refid="classpath" />
        <formatter type="plain" usefile="false" />
        <batchtest>
            <fileset dir="${src}">
                <include name="**/${test}.java" />
            </fileset>
        </batchtest>
    </junit>
</target>

the key line there being the sysproperty tag.

关键是 sysproperty 标签。

And now I can run

现在我可以跑了

ant runtest -Dtest=MyTest -Dtests=testFoo,testBar

as desired. This works with JUnit 4.1 --- in 4.4, subclass from JUnit4ClassRunner, and in 4.5 and later, subclass from BlockJUnit4ClassRunner.

如预期的。这适用于 JUnit 4.1 --- 在 4.4 中,来自 JUnit4ClassRunner 的子类,以及在 4.5 及更高版本中,来自 BlockJUnit4ClassRunner 的子类。

回答by Robert Hyman Will

Since JUnit 4.12 we have @Category annotationsto solve just that problem.

从 JUnit 4.12 开始,我们有@Category 注释来解决这个问题。

回答by guerda

Create your own TestClassMethodsRunner(it's not documentated or I don't find it now).
A TestClassMethodsRunnerexecutes all TestCases and you can set up a filtered TestClassMethodsRunner.

创建您自己的TestClassMethodsRunner(它没有记录或我现在找不到)。
ATestClassMethodsRunner执行所有测试用例,您可以设置一个过滤的TestClassMethodsRunner.

All you have to do is override the TestMethodRunner createMethodRunner(Object, Method, RunNotifier)method. This is a simple an hacky solution:

您所要做的就是覆盖该TestMethodRunner createMethodRunner(Object, Method, RunNotifier)方法。这是一个简单的hacky解决方案:

public class FilteredTestRunner extends TestClassMethodsRunner {

    public FilteredTestRunner(Class<?> aClass) {
        super(aClass);
    }

    @Override
    protected TestMethodRunner createMethodRunner(Object aTest, Method aMethod, RunNotifier aNotifier) {
        if (aTest.getClass().getName().contains("NOT")) {
            return new TestMethodRunner(aTest, aMethod, aNotifier, null) {
                @Override
                public void run() {
                    //do nothing with this test.
                }
            };
        } else {
            return super.createMethodRunner(aTest, aMethod, aNotifier);
        }
    }

}

With this TestRunner, you execute all Tests that don't contain the string "NOT". Others will be ignored :) Just add the @RunWithannotation with your TestRunner class to your test.

使用此 TestRunner,您可以执行所有不包含字符串“NOT”的测试。其他人将被忽略:) 只需将@RunWith带有 TestRunner 类的注释添加到您的测试中即可。

@RunWith(FilteredTestRunner.class)
public class ThisTestsWillNOTBeExecuted {
   //No test is executed.
}

@RunWith(FilteredTestRunner.class)
public class ThisTestsWillBeExecuted {
   //All tests are executed.
}

In the createMethodRunnermethod you can check the current test against a list of tests that must be executed or introduce new criterias.

在该createMethodRunner方法中,您可以根据必须执行的测试列表或引入新标准来检查当前测试。

Good luck with this!

祝你好运!

Hints for a nicer solution are appreciated!

感谢提供更好的解决方案的提示!

回答by tsuna

There is a simpler way for the common case where you need to run only one test method, without having to go through the hassle of creating a custom Runneror Filter:

对于只需要运行一个测试方法的常见情况,有一种更简单的方法,而不必经历创建自定义Runner或的麻烦Filter

public class MyTestClass {

  public static void main(final String[] args) throws Exception {
    final JUnitCore junit = new JUnitCore();
    final String singleTest = // Get the name of test from somewhere (environment, system property, whatever you want).
    final Request req;
    if (singleTest != null) {
      req = Request.method(MyTestClass.class, singleTest);
    } else {
      req = Request.aClass(MyTestClass.class);
    }
    final Result result = junit.run(req);
    // Check result.getFailures etc.
    if (!result.wasSuccessful()) {
      System.exit(1);
    }
  }

  // Your @Test methods here.

}