Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
210 views
in Technique[技术] by (71.8m points)

java - How to define a JUnit method rule in a test suite?

I have a class that is a JUnit suite of JUnit test classes. I would like to define a rule on the suite to do something to the database before and after each unit test is run if a certain annotation is present on that test method.

I've been able to create a @ClassRule in the suites and test classes that will do this before each and every class (which is not good enough) and I have been able to define the test rules with the test classes themselves, but this is repetitive and does not seem very DRY.

Is it possible to define a per-test-method rule in the suite or must I add them to each and every test?

Edit: To clarify, I want to declare code in a suite that will run between (i.e. "around") the test methods in the test classes.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This can be done, but it needs a bit of work. You need to define your own Suite runner and your own Test runner as well, and then override runChild() in the test runner. Using the following:

AllTests.java:

@RunWith(MySuite.class)
@SuiteClasses({Class1Test.class})
public class AllTests {
}

Class1Test.java:

public class Class1Test {
    @Deprecated @Test public void test1() {
        System.out.println("" + this.getClass().getName() + " test1");
    }

    @Test public void test2() {
        System.out.println("" + this.getClass().getName() + " test2");
    }
}

Note that I've annotated test1() with @Deprecated. You want to do something different when you have the @Deprecated annotation on the test, so we need to extend Suite to use a custom Runner:

public class MySuite extends Suite {
    // copied from Suite
    private static Class<?>[] getAnnotatedClasses(Class<?> klass) throws InitializationError {
        Suite.SuiteClasses annotation = klass.getAnnotation(Suite.SuiteClasses.class);
        if (annotation == null) {
            throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", klass.getName()));
        }
        return annotation.value();
    }

    // copied from Suite
    public MySuite(Class<?> klass, RunnerBuilder builder) throws InitializationError {
        super(null, getRunners(getAnnotatedClasses(klass)));
    }

    public static List<Runner> getRunners(Class<?>[] classes) throws InitializationError {
        List<Runner> runners = new LinkedList<Runner>();

        for (Class<?> klazz : classes) {
            runners.add(new MyRunner(klazz));
        }

        return runners;
    }
}

JUnit creates a Runner for each test it will run. Normally, Suite would just create the default BlockJUnit4ClassRunner, all we're doing here is overriding the constructor for the Suite which reads the classes from the SuiteClass annotation and we're creating our own runners with them, MyRunner. This is our MyRunner class:

public class MyRunner extends BlockJUnit4ClassRunner {
    public MyRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
        Description description= describeChild(method);
        if (method.getAnnotation(Ignore.class) != null) {
            notifier.fireTestIgnored(description);
        } else {
            if (description.getAnnotation(Deprecated.class) != null) {
                System.out.println("name=" + description.getMethodName() + " annotations=" + description.getAnnotations());
            }
            runLeaf(methodBlock(method), description, notifier);
        }
    }
}

Most of this is copied from BlockJUnit4ClassRunner. The bit I've added is:

if (description.getAnnotation(Deprecated.class) != null) {
    System.out.println("name=" + description.getMethodName() + " annotations=" + description.getAnnotations());
}

where we test for the existence of the @Deprecated annotation on the method, and do something if it's there. The rest is left as an exercise for the reader. When I run the above Suite, I get as output:

name=test1 annotations=[@java.lang.Deprecated(), @org.junit.Test(expected=class org.junit.Test$None, timeout=0)]
uk.co.farwell.junit.run.Class1Test test1
uk.co.farwell.junit.run.Class1Test test2

Please note that Suite has multiple constructors depending upon how it is invoked. The above works with Eclipse, but I haven't tested other ways of running the Suite. See the comments alongside the various constructors for Suite for more information.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...