运行JUnit @Test方法的子集

Geo*_*rge 14 java reflection junit unit-testing

我们使用JUnit 4来测试:我们有不是子类的类TestCase,并且它们有公共方法注释@Test.我们有一个文件有很多@Test方法.能够通过命令行中的Ant运行它们的子集会很好,这是JUnit 3的这个配方的样式:

ant runtest -Dtest=MyTest -Dtests=testFoo,testBar
Run Code Online (Sandbox Code Playgroud)

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

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

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

谢谢你的时间.

Rob*_*ill 14

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

  • @Category很酷,但只有你已经建立套房.正如文章最后提到的那样,并不适合每个人的测试环境.他们绝对值得指出,对于一些幸运的人来说,这将彻底解决他们的问题. (2认同)
  • 从Surefire 2.11(左右)开始,我们可以使用-Dgroups按类别选择测试,而无需构建套件.由于这种改进,我很高兴上周删除了很多我们的测试套件!(Maven Surefire插件的<groups>参数仅针对TestNG进行了记录,但由于2.11也适用于JUnit.) (2认同)

Geo*_*rge 10

guerda的解决方案很好.这就是我最终做的事情(这是我之前联系过的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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

@RunWith(FilteredRunner.class)
public class MyTest {
Run Code Online (Sandbox Code Playgroud)

并将以下内容放在我的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>
Run Code Online (Sandbox Code Playgroud)

有关sysproperty标记的关键行.

现在我可以跑了

ant runtest -Dtest=MyTest -Dtests=testFoo,testBar
Run Code Online (Sandbox Code Playgroud)

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


gue*_*rda 8

创建自己的TestClassMethodsRunner(它没有记录,或者我现在没有找到它).
A TestClassMethodsRunner执行所有TestCases,您可以设置过滤TestClassMethodsRunner.

您所要做的就是覆盖该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);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

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

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

@RunWith(FilteredTestRunner.class)
public class ThisTestsWillBeExecuted {
   //All tests are executed.
}
Run Code Online (Sandbox Code Playgroud)

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

祝你好运!

我们非常感谢提供更好的解决方案!