Whoosajiggawha Whoosajiggawha - 1 month ago 18
Java Question

Run JUnit setup and teardown exactly once for any test(s) run

Before every JUnit test I run certain things need to be set up. Some properties need to be loaded, a database connection needs to be made, a separate J2SE application needs to be started, etc. When every single test has finished the database connection can be killed and J2SE application shut down.

I can accomplish this by using @BeforeClass and @AfterClass annotations in a test suite, but that limits me to only being able to run tests inside the suite. If I want to run an individual test outside of a suite it won't run the suite setup and teardown methods. Likewise, if I want to run an individual test method (through an IDE) it won't run the setup and teardown from the suite.

Is there a way to setup JUnit tests so that, no matter how they're run, through a suite, or a test case, or an individual method, they always run a setup method only once before running anything, and a teardown only once after every test has been executed? Having all of the test cases extend an abstract class with a static initializer solves the setup problem, but not teardown.

Answer

I was able to accomplish what I needed using two separate methods. I'd prefer if just one method worked, but this will do...

I have a custom RunListener class that implements testRunFinished:

public class MyRunListener extends RunListener
{
    @Override
    public void testRunFinished(Result result) throws Exception
    {
        //Do whatever teardown needs to be done
    }
}

I then have a custom BlockJUnit4ClassRunner class with the following run() method:

private static boolean listenerAdded = false;
@Override 
public void run(RunNotifier notifier)
{
    //listenerAdded required or else the listener will be added once for every test case, and testRunFinished will be run multiple times
    if(!listenerAdded)
    {
        listenerAdded = true;
        notifier.addListener(new MyRunListener());
    }
    super.run(notifier);
}

An abstract test case class uses the annotation:

@RunWith(MyRunner.class)

I was hoping that I could also implement testRunStarted in MyRunListener but it didn't work. Apparently adding the listener like this in MyRunner, the listener isn't added until after the testRunStarted method would have been run, so it isn't being executed. The workaround for this is, as mentioned in the original question, to use a static initilizer in AbstractTestCase:

@RunWith(MyRunner.class)
public class AbstractTestCase
{
    private static boolean setupDone = false;
    static
    {
        if(!setupDone)
        {
            setupDone = true;
            //Do whatever setup needs to be done
        }
    }
...

If anyone knows of a way to add MyRunListener that will allow the use of testRunStarted that would be nice, but in the meantime this works.